├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ └── maven-wrapper.properties ├── README.md ├── aws ├── apps │ ├── application-configmap.yaml │ ├── fashion-bestseller-deployment.yaml │ ├── gateway-deployment.yaml │ ├── hot-deals-deployment.yaml │ ├── toys-bestseller-configmap.yml │ ├── toys-bestseller-deployment.yaml │ └── toys-bestseller-secret.yaml └── infra │ ├── configserver-deployment.yaml │ ├── configurationwatcher-configmap.yaml │ ├── conigurationwatcher-deployment.yaml │ └── discoveryserver-deployment.yaml ├── configserver ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── example │ │ └── chaos │ │ └── monkey │ │ └── shopping │ │ └── bestseller │ │ └── configserver │ │ └── ShoppingConfigServerApplication.java │ └── resources │ └── application.yaml ├── discoveryserver ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── example │ │ └── chaos │ │ └── monkey │ │ └── shopping │ │ └── bestseller │ │ └── discoveryserver │ │ └── DiscoveryServerApplication.java │ └── resources │ └── application.yaml ├── fashion-bestseller ├── k8s │ └── deployment.yaml ├── pom.xml ├── skaffold.yaml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── chaos │ │ │ └── monkey │ │ │ └── shopping │ │ │ └── bestseller │ │ │ └── fashion │ │ │ ├── BestsellerFashionApplication.java │ │ │ └── BestsellerFashionRestController.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── chaos │ └── monkey │ └── shopping │ └── bestseller │ └── fashion │ └── BestsellerFashionApplicationTest.java ├── gateway ├── k8s │ └── deployment.yaml ├── pom.xml ├── skaffold.yaml └── src │ └── main │ ├── java │ └── com │ │ └── example │ │ └── chaos │ │ └── monkey │ │ └── shopping │ │ └── gateway │ │ ├── GatewayApplication.java │ │ ├── StartPageController.java │ │ └── domain │ │ ├── ProductResponse.java │ │ ├── ResponseType.java │ │ └── Startpage.java │ └── resources │ └── application.yml ├── hot-deals ├── k8s │ └── deployment.yaml ├── pom.xml ├── skaffold.yaml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── chaos │ │ │ └── monkey │ │ │ └── shopping │ │ │ └── hotdeals │ │ │ ├── HotDealsApplication.java │ │ │ └── HotDealsRestController.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── chaos │ └── monkey │ └── shopping │ └── hotdeals │ └── HotDealsApplicationTest.java ├── mvnw ├── mvnw.cmd ├── pom.xml ├── shared ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── example │ └── chaos │ └── monkey │ └── shopping │ └── domain │ ├── Product.java │ ├── ProductBuilder.java │ └── ProductCategory.java └── toys-bestseller ├── configmap.yml ├── k8s └── deployment.yaml ├── pom.xml ├── skaffold.yaml └── src ├── main ├── java │ └── com │ │ └── example │ │ └── chaos │ │ └── monkey │ │ └── shopping │ │ └── bestseller │ │ └── toys │ │ ├── BestsellerToysApplication.java │ │ └── BestsellerToysRestController.java └── resources │ └── application.yml └── test └── java └── com └── example └── chaos └── monkey └── shopping └── bestseller └── toys └── BestsellerToysApplicationTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | 7 | # Package Files # 8 | *.jar 9 | *.war 10 | *.ear 11 | 12 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 13 | hs_err_pid* 14 | .idea/ 15 | *.iml 16 | *.iws 17 | 18 | *.log 19 | maven-archiver* 20 | target/ 21 | 22 | #flattened POMs 23 | .flattened-pom.xml 24 | 25 | # gnupg keyring 26 | /.gnupg 27 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | https://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | import java.io.File; 21 | import java.io.FileInputStream; 22 | import java.io.FileOutputStream; 23 | import java.io.IOException; 24 | import java.net.URL; 25 | import java.nio.channels.Channels; 26 | import java.nio.channels.ReadableByteChannel; 27 | import java.util.Properties; 28 | 29 | public class MavenWrapperDownloader { 30 | 31 | /** 32 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 33 | */ 34 | private static final String DEFAULT_DOWNLOAD_URL = 35 | "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; 36 | 37 | /** 38 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 39 | * use instead of the default one. 40 | */ 41 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 42 | ".mvn/wrapper/maven-wrapper.properties"; 43 | 44 | /** 45 | * Path where the maven-wrapper.jar will be saved to. 46 | */ 47 | private static final String MAVEN_WRAPPER_JAR_PATH = 48 | ".mvn/wrapper/maven-wrapper.jar"; 49 | 50 | /** 51 | * Name of the property which should be used to override the default download url for the wrapper. 52 | */ 53 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 54 | 55 | public static void main(String args[]) { 56 | System.out.println("- Downloader started"); 57 | File baseDirectory = new File(args[0]); 58 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 59 | 60 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 61 | // wrapperUrl parameter. 62 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 63 | String url = DEFAULT_DOWNLOAD_URL; 64 | if (mavenWrapperPropertyFile.exists()) { 65 | FileInputStream mavenWrapperPropertyFileInputStream = null; 66 | try { 67 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 68 | Properties mavenWrapperProperties = new Properties(); 69 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 70 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 71 | } 72 | catch (IOException e) { 73 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 74 | } 75 | finally { 76 | try { 77 | if (mavenWrapperPropertyFileInputStream != null) { 78 | mavenWrapperPropertyFileInputStream.close(); 79 | } 80 | } 81 | catch (IOException e) { 82 | // Ignore ... 83 | } 84 | } 85 | } 86 | System.out.println("- Downloading from: : " + url); 87 | 88 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 89 | if (!outputFile.getParentFile().exists()) { 90 | if (!outputFile.getParentFile().mkdirs()) { 91 | System.out.println( 92 | "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 93 | } 94 | } 95 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 96 | try { 97 | downloadFileFromURL(url, outputFile); 98 | System.out.println("Done"); 99 | System.exit(0); 100 | } 101 | catch (Throwable e) { 102 | System.out.println("- Error downloading"); 103 | e.printStackTrace(); 104 | System.exit(1); 105 | } 106 | } 107 | 108 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 109 | URL website = new URL(urlString); 110 | ReadableByteChannel rbc; 111 | rbc = Channels.newChannel(website.openStream()); 112 | FileOutputStream fos = new FileOutputStream(destination); 113 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 114 | fos.close(); 115 | rbc.close(); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chaos-monkey-spring-boot-demo 2 | Demo of Chaos Monkey for Spring Boot Using Spring Cloud Kubernetes 3 | 4 | # Building The Containers 5 | 6 | Each project contains a Dockerfile that will create a container for the app. You can do this easily 7 | by running `./mvn clean package -Pdocker`. 8 | 9 | **NOTE** You should edit the parent POM file and change the value of `docker.image.prefix` to 10 | your own prefix. 11 | 12 | After building the container images you will need to push them to a repository that your Kubernetes 13 | deployment has access to, for example Docker Hub. 14 | 15 | # Deploying To Kubernetes 16 | 17 | ## Testing The Apps 18 | 19 | Each app is exposes a `nodeport`, you will need to find the `nodeport` for each application in your deployment. 20 | If using MiniKube you can easily open the service by running `minikube service `. For example 21 | 22 | ``` 23 | $ minikube service gateway 24 | ``` 25 | 26 | ### Toys Bestseller 27 | 28 | This app exposes one endpoint at `/toys/bestseller` which accepts a `GET` request and returns JSON 29 | representing the best selling toys. 30 | 31 | In addition all actuator endpoints are exposed, this is useful for demonstrationg the Spring Cloud Kubernetes 32 | integration with ConfigMaps. 33 | 34 | The app uses Chaos Monkey for Spring Boot to inject random latency into `RestController`s. It is disabled by 35 | default but can be enabled by changing the value of the ConfigMap you created above. 36 | 37 | If you hit `/actuator/env` you should see the ConfigMap in the list of `PropertySources` for the app. Edit the 38 | ConfigMap in Kubernetes changing `chaos.monkey.enabled` from `false` to `true`. Now hit `/actuator/env` again 39 | and you should see the property change. 40 | 41 | Now when you hit `/toys/bestseller` you should randomly see the response take a bit to come back. This will 42 | be useful when demoing Hystrix via the gateway. 43 | 44 | In addition since the Actuator endpoints are enabled it is useful to hit `/actuator/health` in this app 45 | to show the health endpoint integration with Kubernetes. 46 | 47 | ### Fashion Bestseller 48 | 49 | This app exposes one endpoint at `/fashion/bestseller` which accepts a `GET` request and returns JSON 50 | representing the best selling fashion items. 51 | 52 | ### Hot Deals 53 | 54 | This app exposes one endpoint at `/hotdeals` which accepts a `GET` request and returns JSON representing 55 | deals from the fashion and toys service. 56 | 57 | ### Gateway 58 | 59 | An API gateway to the toys, fashion, and hotdeals services. There are routes that point to eat `/toys/**`, 60 | `/fashion/**` and `/hotdeals**`. Each of these endpoints is protected by a Hystrix circuitbreaker tha when tripped 61 | returns an empty array and the response contains a header called `fallback` with the value of `true`. 62 | 63 | In addition there is another endpoint at `/startpage` which aggregates the responses from all the services. 64 | 65 | When Chaos Monkey for Spring Boot is enabled on the toys service occationally the Hystrix circuit breaker will 66 | be tripped when the latency is injected. 67 | -------------------------------------------------------------------------------- /aws/apps/application-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | application.yaml: |- 4 | management: 5 | endpoints: 6 | web: 7 | exposure: 8 | include: "*" 9 | kind: ConfigMap 10 | metadata: 11 | name: application 12 | -------------------------------------------------------------------------------- /aws/apps/fashion-bestseller-deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: Service 7 | metadata: 8 | labels: 9 | app: fashion-bestseller 10 | name: fashion-bestseller 11 | spec: 12 | ports: 13 | - name: http 14 | port: 8082 15 | targetPort: 8082 16 | selector: 17 | app: fashion-bestseller 18 | type: ClusterIP 19 | - apiVersion: apps/v1 20 | kind: Deployment 21 | metadata: 22 | name: fashion-bestseller-deployment 23 | spec: 24 | selector: 25 | matchLabels: 26 | app: fashion-bestseller 27 | template: 28 | metadata: 29 | labels: 30 | app: fashion-bestseller 31 | spec: 32 | containers: 33 | - name: fashion-bestseller 34 | image: 833443578445.dkr.ecr.us-east-2.amazonaws.com/fashion-bestseller:1.0-SNAPSHOT 35 | imagePullPolicy: Always 36 | readinessProbe: 37 | httpGet: 38 | port: 8082 39 | path: /actuator/health/readiness 40 | livenessProbe: 41 | httpGet: 42 | port: 8082 43 | path: /actuator/health/liveness 44 | ports: 45 | - containerPort: 8082 46 | -------------------------------------------------------------------------------- /aws/apps/gateway-deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: Service 7 | metadata: 8 | labels: 9 | app: gateway 10 | name: gateway 11 | spec: 12 | ports: 13 | - name: http 14 | port: 8070 15 | targetPort: 8070 16 | selector: 17 | app: gateway 18 | type: LoadBalancer 19 | - apiVersion: apps/v1 20 | kind: Deployment 21 | metadata: 22 | name: gateway-deployment 23 | spec: 24 | selector: 25 | matchLabels: 26 | app: gateway 27 | template: 28 | metadata: 29 | labels: 30 | app: gateway 31 | spec: 32 | containers: 33 | - name: gateway 34 | image: 833443578445.dkr.ecr.us-east-2.amazonaws.com/gateway:1.0-SNAPSHOT 35 | imagePullPolicy: Always 36 | readinessProbe: 37 | httpGet: 38 | port: 8070 39 | path: /actuator/health/readiness 40 | livenessProbe: 41 | httpGet: 42 | port: 8070 43 | path: /actuator/health/liveness 44 | ports: 45 | - containerPort: 8070 46 | -------------------------------------------------------------------------------- /aws/apps/hot-deals-deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: Service 7 | metadata: 8 | labels: 9 | app: hot-deals 10 | name: hot-deals 11 | spec: 12 | ports: 13 | - name: http 14 | port: 8083 15 | targetPort: 8083 16 | selector: 17 | app: hot-deals 18 | type: ClusterIP 19 | - apiVersion: apps/v1 20 | kind: Deployment 21 | metadata: 22 | name: hot-deals-deployment 23 | spec: 24 | selector: 25 | matchLabels: 26 | app: hot-deals 27 | template: 28 | metadata: 29 | labels: 30 | app: hot-deals 31 | spec: 32 | containers: 33 | - name: hot-deals 34 | image: 833443578445.dkr.ecr.us-east-2.amazonaws.com/hot-deals:1.0-SNAPSHOT 35 | imagePullPolicy: Always 36 | readinessProbe: 37 | httpGet: 38 | port: 8083 39 | path: /actuator/health/readiness 40 | livenessProbe: 41 | httpGet: 42 | port: 8083 43 | path: /actuator/health/liveness 44 | ports: 45 | - containerPort: 8083 46 | -------------------------------------------------------------------------------- /aws/apps/toys-bestseller-configmap.yml: -------------------------------------------------------------------------------- 1 | kind: ConfigMap 2 | apiVersion: v1 3 | metadata: 4 | name: toys-bestseller 5 | labels: 6 | "spring.cloud.kubernetes.config": "true" 7 | data: 8 | chaos.monkey.enabled: "false" 9 | chaos.monkey.assaults.latencyActive: "true" -------------------------------------------------------------------------------- /aws/apps/toys-bestseller-deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: Service 7 | metadata: 8 | labels: 9 | app: toys-bestseller 10 | name: toys-bestseller 11 | spec: 12 | ports: 13 | - name: http 14 | port: 8081 15 | targetPort: 8081 16 | selector: 17 | app: toys-bestseller 18 | type: LoadBalancer 19 | - apiVersion: apps/v1 20 | kind: Deployment 21 | metadata: 22 | name: toys-bestseller-deployment 23 | spec: 24 | selector: 25 | matchLabels: 26 | app: toys-bestseller 27 | template: 28 | metadata: 29 | labels: 30 | app: toys-bestseller 31 | spec: 32 | containers: 33 | - name: toys-bestseller 34 | image: 833443578445.dkr.ecr.us-east-2.amazonaws.com/toys-bestseller:1.0-SNAPSHOT 35 | imagePullPolicy: Always 36 | readinessProbe: 37 | httpGet: 38 | port: 8081 39 | path: /actuator/health/readiness 40 | livenessProbe: 41 | httpGet: 42 | port: 8081 43 | path: /actuator/health/liveness 44 | ports: 45 | - containerPort: 8081 46 | -------------------------------------------------------------------------------- /aws/apps/toys-bestseller-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: toys-bestseller 5 | type: Opaque 6 | data: 7 | username: YWRtaW4= 8 | password: MWYyZDFlMmU2N2Rm -------------------------------------------------------------------------------- /aws/infra/configserver-deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | data: 7 | application.yaml: |- 8 | spring: 9 | profiles: 10 | include: "git" 11 | cloud: 12 | config: 13 | server: 14 | git: 15 | uri: "https://github.com/ryanjbaxter/s1-2021-config" 16 | kind: ConfigMap 17 | metadata: 18 | name: kubernetesconfigserver 19 | - apiVersion: v1 20 | kind: Service 21 | metadata: 22 | labels: 23 | app: spring-cloud-kubernetes-configserver 24 | name: spring-cloud-kubernetes-configserver 25 | spec: 26 | ports: 27 | - name: http 28 | port: 8888 29 | targetPort: 8888 30 | selector: 31 | app: spring-cloud-kubernetes-configserver 32 | type: LoadBalancer 33 | - apiVersion: v1 34 | kind: ServiceAccount 35 | metadata: 36 | labels: 37 | app: spring-cloud-kubernetes-configserver 38 | name: spring-cloud-kubernetes-configserver 39 | - apiVersion: rbac.authorization.k8s.io/v1 40 | kind: RoleBinding 41 | metadata: 42 | labels: 43 | app: spring-cloud-kubernetes-configserver 44 | name: spring-cloud-kubernetes-configserver:view 45 | roleRef: 46 | kind: Role 47 | apiGroup: rbac.authorization.k8s.io 48 | name: namespace-reader 49 | subjects: 50 | - kind: ServiceAccount 51 | name: spring-cloud-kubernetes-configserver 52 | - apiVersion: rbac.authorization.k8s.io/v1 53 | kind: Role 54 | metadata: 55 | namespace: s1-2021 56 | name: namespace-reader 57 | rules: 58 | - apiGroups: ["", "extensions", "apps"] 59 | resources: ["configmaps", "pods", "services", "endpoints", "secrets"] 60 | verbs: ["get", "list", "watch"] 61 | - apiVersion: apps/v1 62 | kind: Deployment 63 | metadata: 64 | name: spring-cloud-kubernetes-configserver-deployment 65 | spec: 66 | selector: 67 | matchLabels: 68 | app: spring-cloud-kubernetes-configserver 69 | template: 70 | metadata: 71 | labels: 72 | app: spring-cloud-kubernetes-configserver 73 | spec: 74 | serviceAccount: spring-cloud-kubernetes-configserver 75 | containers: 76 | - name: spring-cloud-kubernetes-configserver 77 | image: 833443578445.dkr.ecr.us-east-2.amazonaws.com/spring-cloud-kubernetes-configserver:2.0.4-SNAPSHOT 78 | imagePullPolicy: Always 79 | readinessProbe: 80 | httpGet: 81 | port: 8888 82 | path: /actuator/health/readiness 83 | livenessProbe: 84 | httpGet: 85 | port: 8888 86 | path: /actuator/health/liveness 87 | ports: 88 | - containerPort: 8888 89 | -------------------------------------------------------------------------------- /aws/infra/configurationwatcher-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | application.yaml: |- 4 | spring: 5 | cloud: 6 | kubernetes: 7 | configuration: 8 | watcher: 9 | refreshDelay: 10 10 | rabbitmq: 11 | username: rbaxter 12 | password: password 13 | logging: 14 | level: 15 | org: 16 | springframework: 17 | cloud: 18 | kubernetes: TRACE 19 | management: 20 | endpoints: 21 | web: 22 | exposure: 23 | include: "*" 24 | kind: ConfigMap 25 | metadata: 26 | name: spring-cloud-kubernetes-configuration-watcher 27 | -------------------------------------------------------------------------------- /aws/infra/conigurationwatcher-deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: Service 7 | metadata: 8 | labels: 9 | app: spring-cloud-kubernetes-configuration-watcher 10 | name: spring-cloud-kubernetes-configuration-watcher 11 | spec: 12 | ports: 13 | - name: http 14 | port: 8888 15 | targetPort: 8888 16 | selector: 17 | app: spring-cloud-kubernetes-configuration-watcher 18 | type: ClusterIP 19 | - apiVersion: v1 20 | kind: ServiceAccount 21 | metadata: 22 | labels: 23 | app: spring-cloud-kubernetes-configuration-watcher 24 | name: spring-cloud-kubernetes-configuration-watcher 25 | - apiVersion: rbac.authorization.k8s.io/v1 26 | kind: RoleBinding 27 | metadata: 28 | labels: 29 | app: spring-cloud-kubernetes-configuration-watcher 30 | name: spring-cloud-kubernetes-configuration-watcher:view 31 | roleRef: 32 | kind: Role 33 | apiGroup: rbac.authorization.k8s.io 34 | name: namespace-reader 35 | subjects: 36 | - kind: ServiceAccount 37 | name: spring-cloud-kubernetes-configuration-watcher 38 | - apiVersion: rbac.authorization.k8s.io/v1 39 | kind: Role 40 | metadata: 41 | namespace: s1-2021 42 | name: namespace-reader 43 | rules: 44 | - apiGroups: ["", "extensions", "apps"] 45 | resources: ["configmaps", "pods", "services", "endpoints", "secrets"] 46 | verbs: ["get", "list", "watch"] 47 | - apiVersion: apps/v1 48 | kind: Deployment 49 | metadata: 50 | name: spring-cloud-kubernetes-configuration-watcher-deployment 51 | spec: 52 | selector: 53 | matchLabels: 54 | app: spring-cloud-kubernetes-configuration-watcher 55 | template: 56 | metadata: 57 | labels: 58 | app: spring-cloud-kubernetes-configuration-watcher 59 | spec: 60 | serviceAccount: spring-cloud-kubernetes-configuration-watcher 61 | containers: 62 | - name: spring-cloud-kubernetes-configuration-watcher 63 | image: springcloud/spring-cloud-kubernetes-configuration-watcher:2.0.4-SNAPSHOT 64 | imagePullPolicy: IfNotPresent 65 | readinessProbe: 66 | httpGet: 67 | port: 8888 68 | path: /actuator/health/readiness 69 | livenessProbe: 70 | httpGet: 71 | port: 8888 72 | path: /actuator/health/liveness 73 | ports: 74 | - containerPort: 8888 75 | -------------------------------------------------------------------------------- /aws/infra/discoveryserver-deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: Service 7 | metadata: 8 | labels: 9 | app: spring-cloud-kubernetes-discoveryserver 10 | name: spring-cloud-kubernetes-discoveryserver 11 | spec: 12 | ports: 13 | - name: http 14 | port: 80 15 | targetPort: 8761 16 | selector: 17 | app: spring-cloud-kubernetes-discoveryserver 18 | type: LoadBalancer 19 | - apiVersion: v1 20 | kind: ServiceAccount 21 | metadata: 22 | labels: 23 | app: spring-cloud-kubernetes-discoveryserver 24 | name: spring-cloud-kubernetes-discoveryserver 25 | - apiVersion: rbac.authorization.k8s.io/v1 26 | kind: RoleBinding 27 | metadata: 28 | labels: 29 | app: spring-cloud-kubernetes-discoveryserver 30 | name: spring-cloud-kubernetes-discoveryserver:view 31 | roleRef: 32 | kind: Role 33 | apiGroup: rbac.authorization.k8s.io 34 | name: namespace-reader 35 | subjects: 36 | - kind: ServiceAccount 37 | name: spring-cloud-kubernetes-discoveryserver 38 | - apiVersion: rbac.authorization.k8s.io/v1 39 | kind: Role 40 | metadata: 41 | namespace: s1-2021 42 | name: namespace-reader 43 | rules: 44 | - apiGroups: ["", "extensions", "apps"] 45 | resources: ["configmaps", "pods", "services", "endpoints", "secrets"] 46 | verbs: ["get", "list", "watch"] 47 | - apiVersion: apps/v1 48 | kind: Deployment 49 | metadata: 50 | name: spring-cloud-kubernetes-discoveryserver-deployment 51 | spec: 52 | selector: 53 | matchLabels: 54 | app: spring-cloud-kubernetes-discoveryserver 55 | template: 56 | metadata: 57 | labels: 58 | app: spring-cloud-kubernetes-discoveryserver 59 | spec: 60 | serviceAccount: spring-cloud-kubernetes-discoveryserver 61 | containers: 62 | - name: spring-cloud-kubernetes-discoveryserver 63 | image: 833443578445.dkr.ecr.us-east-2.amazonaws.com/spring-cloud-kubernetes-discoveryserver:2.0.4-SNAPSHOT 64 | imagePullPolicy: Always 65 | readinessProbe: 66 | httpGet: 67 | port: 8761 68 | path: /actuator/health/readiness 69 | livenessProbe: 70 | httpGet: 71 | port: 8761 72 | path: /actuator/health/liveness 73 | ports: 74 | - containerPort: 8761 75 | -------------------------------------------------------------------------------- /configserver/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | shopping-parent 7 | com.example.chaos.monkey.shopping 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | configserver 13 | 14 | 15 | 16 | org.springframework.cloud 17 | spring-cloud-config-server 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /configserver/src/main/java/com/example/chaos/monkey/shopping/bestseller/configserver/ShoppingConfigServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.bestseller.configserver; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.config.server.EnableConfigServer; 6 | 7 | /** 8 | * @author Ryan Baxter 9 | */ 10 | @EnableConfigServer 11 | @SpringBootApplication 12 | public class ShoppingConfigServerApplication { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(ShoppingConfigServerApplication.class, args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /configserver/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | 2 | spring: 3 | profiles: 4 | active: git 5 | cloud: 6 | config: 7 | server: 8 | git: 9 | uri: https://github.com/ryanjbaxter/s1-2021-config 10 | server: 11 | port: 8888 -------------------------------------------------------------------------------- /discoveryserver/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | shopping-parent 7 | com.example.chaos.monkey.shopping 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | discoveryserver 13 | 14 | 15 | 16 | org.springframework.cloud 17 | spring-cloud-starter-netflix-eureka-server 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /discoveryserver/src/main/java/com/example/chaos/monkey/shopping/bestseller/discoveryserver/DiscoveryServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.bestseller.discoveryserver; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 | 7 | /** 8 | * @author Ryan Baxter 9 | */ 10 | @EnableEurekaServer 11 | @SpringBootApplication 12 | public class DiscoveryServerApplication { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(DiscoveryServerApplication.class, args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /discoveryserver/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8761 3 | 4 | eureka: 5 | instance: 6 | hostname: localhost 7 | client: 8 | registerWithEureka: false 9 | fetchRegistry: false 10 | serviceUrl: 11 | defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ -------------------------------------------------------------------------------- /fashion-bestseller/k8s/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: Service 7 | metadata: 8 | labels: 9 | app: fashion-bestseller 10 | name: fashion-bestseller 11 | spec: 12 | ports: 13 | - name: http 14 | port: 8082 15 | targetPort: 8082 16 | selector: 17 | app: fashion-bestseller 18 | type: ClusterIP 19 | - apiVersion: v1 20 | kind: ServiceAccount 21 | metadata: 22 | labels: 23 | app: spring-cloud-kubernetes-configuration-watcher 24 | name: spring-cloud-kubernetes-configuration-watcher 25 | - apiVersion: rbac.authorization.k8s.io/v1 26 | kind: RoleBinding 27 | metadata: 28 | labels: 29 | app: spring-cloud-kubernetes-configuration-watcher 30 | name: spring-cloud-kubernetes-configuration-watcher:view 31 | roleRef: 32 | kind: Role 33 | apiGroup: rbac.authorization.k8s.io 34 | name: namespace-reader 35 | subjects: 36 | - kind: ServiceAccount 37 | name: spring-cloud-kubernetes-configuration-watcher 38 | - apiVersion: rbac.authorization.k8s.io/v1 39 | kind: Role 40 | metadata: 41 | namespace: default 42 | name: namespace-reader 43 | rules: 44 | - apiGroups: ["", "extensions", "apps"] 45 | resources: ["configmaps", "pods", "services", "endpoints", "secrets"] 46 | verbs: ["get", "list", "watch"] 47 | - apiVersion: apps/v1 48 | kind: Deployment 49 | metadata: 50 | name: fashion-bestseller-deployment 51 | spec: 52 | selector: 53 | matchLabels: 54 | app: fashion-bestseller 55 | template: 56 | metadata: 57 | labels: 58 | app: fashion-bestseller 59 | spec: 60 | serviceAccount: spring-cloud-kubernetes-configuration-watcher 61 | containers: 62 | - name: fashion-bestseller 63 | image: springone/fashion-bestseller 64 | imagePullPolicy: IfNotPresent 65 | readinessProbe: 66 | httpGet: 67 | port: 8082 68 | path: /actuator/health/readiness 69 | livenessProbe: 70 | httpGet: 71 | port: 8082 72 | path: /actuator/health/liveness 73 | ports: 74 | - containerPort: 8082 75 | -------------------------------------------------------------------------------- /fashion-bestseller/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | shopping-parent 7 | com.example.chaos.monkey.shopping 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | fashion-bestseller 13 | fashion 14 | 15 | 16 | 17 | de.codecentric 18 | chaos-monkey-spring-boot 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-actuator 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | org.springframework.cloud 30 | spring-cloud-starter-netflix-eureka-client 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-devtools 35 | runtime 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-test 40 | test 41 | 42 | 43 | com.example.chaos.monkey.shopping 44 | shared 45 | ${parent.version} 46 | 47 | 48 | org.springframework.cloud 49 | spring-cloud-config-client 50 | 51 | 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-maven-plugin 57 | 58 | 59 | ${env.IMAGE} 60 | 61 | build-image 62 | 63 | 64 | 65 | package 66 | 67 | build-image 68 | 69 | 70 | 71 | 72 | 73 | com.google.cloud.tools 74 | jib-maven-plugin 75 | 3.1.4 76 | 77 | 78 | 833443578445.dkr.ecr.us-east-2.amazonaws.com/fashion-bestseller 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /fashion-bestseller/skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v2alpha3 2 | kind: Config 3 | metadata: 4 | name: fashion-bestseller 5 | build: 6 | # artifacts: 7 | # - image: ryanjbaxter/fashion-bestseller 8 | # tagPolicy: 9 | # envTemplate: 10 | # template: "{{.IMAGE_NAME}}:{{.DIGEST_HEX}}" 11 | artifacts: 12 | - image: springone/fashion-bestseller 13 | custom: 14 | buildCommand: "../mvnw clean install" 15 | dependencies: 16 | paths: 17 | - src 18 | - pom.xml 19 | # local: 20 | 21 | # jib: { 22 | # args: ["-Pjib"] 23 | # } 24 | deploy: 25 | kubectl: 26 | manifests: 27 | - k8s/deployment.yaml 28 | -------------------------------------------------------------------------------- /fashion-bestseller/src/main/java/com/example/chaos/monkey/shopping/bestseller/fashion/BestsellerFashionApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.bestseller.fashion; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | 7 | @SpringBootApplication 8 | @EnableDiscoveryClient 9 | ///thisdfvdfasd 10 | public class BestsellerFashionApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(BestsellerFashionApplication.class, args); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /fashion-bestseller/src/main/java/com/example/chaos/monkey/shopping/bestseller/fashion/BestsellerFashionRestController.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.bestseller.fashion; 2 | 3 | import com.example.chaos.monkey.shopping.domain.Product; 4 | import com.example.chaos.monkey.shopping.domain.ProductBuilder; 5 | import com.example.chaos.monkey.shopping.domain.ProductCategory; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import java.util.concurrent.atomic.AtomicLong; 13 | 14 | /** 15 | * @author Benjamin Wilms 16 | */ 17 | @RestController 18 | @RequestMapping("/fashion") 19 | public class BestsellerFashionRestController { 20 | 21 | @GetMapping("/bestseller") 22 | public List getBestsellerProducts() { 23 | AtomicLong aLong = new AtomicLong(4); 24 | 25 | ProductBuilder productBuilder = new ProductBuilder(); 26 | 27 | Product product1 = productBuilder.setCategory(ProductCategory.FASHION).setId(aLong.getAndIncrement()).setName("Bob Mailor Slim Jeans") 28 | .createProduct(); 29 | 30 | Product product2 = productBuilder.setCategory(ProductCategory.FASHION).setId(aLong.getAndIncrement()).setName("Lewi's Jeanshose 511 " + 31 | "Slim Fit") 32 | .createProduct(); 33 | 34 | Product product3 = productBuilder.setCategory(ProductCategory.FASHION).setId(aLong.getAndIncrement()).setName("Urban Classics T-Shirt " + 35 | "Shaped Long Tee") 36 | .createProduct(); 37 | return Arrays.asList(product1, product2, product3); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /fashion-bestseller/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8082 3 | 4 | spring: 5 | application: 6 | name: fashion-bestseller 7 | config: 8 | import: configserver:http://localhost:8888 9 | 10 | --- 11 | spring: 12 | config: 13 | activate: 14 | on-cloud-platform: kubernetes 15 | import: configserver:http://spring-cloud-kubernetes-configserver:8888 -------------------------------------------------------------------------------- /fashion-bestseller/src/test/java/com/example/chaos/monkey/shopping/bestseller/fashion/BestsellerFashionApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.bestseller.fashion; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | /** 8 | * @author Benjamin Wilms 9 | */ 10 | @SpringBootTest 11 | public class BestsellerFashionApplicationTest { 12 | @Test 13 | public void contextLoads() { 14 | } 15 | } -------------------------------------------------------------------------------- /gateway/k8s/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: Service 7 | metadata: 8 | labels: 9 | app: gateway 10 | name: gateway 11 | spec: 12 | ports: 13 | - name: http 14 | port: 8070 15 | targetPort: 8070 16 | selector: 17 | app: gateway 18 | type: ClusterIP 19 | - apiVersion: v1 20 | kind: ServiceAccount 21 | metadata: 22 | labels: 23 | app: spring-cloud-kubernetes-configuration-watcher 24 | name: spring-cloud-kubernetes-configuration-watcher 25 | - apiVersion: rbac.authorization.k8s.io/v1 26 | kind: RoleBinding 27 | metadata: 28 | labels: 29 | app: spring-cloud-kubernetes-configuration-watcher 30 | name: spring-cloud-kubernetes-configuration-watcher:view 31 | roleRef: 32 | kind: Role 33 | apiGroup: rbac.authorization.k8s.io 34 | name: namespace-reader 35 | subjects: 36 | - kind: ServiceAccount 37 | name: spring-cloud-kubernetes-configuration-watcher 38 | - apiVersion: rbac.authorization.k8s.io/v1 39 | kind: Role 40 | metadata: 41 | namespace: default 42 | name: namespace-reader 43 | rules: 44 | - apiGroups: ["", "extensions", "apps"] 45 | resources: ["configmaps", "pods", "services", "endpoints", "secrets"] 46 | verbs: ["get", "list", "watch"] 47 | - apiVersion: apps/v1 48 | kind: Deployment 49 | metadata: 50 | name: gateway-deployment 51 | spec: 52 | selector: 53 | matchLabels: 54 | app: gateway 55 | template: 56 | metadata: 57 | labels: 58 | app: gateway 59 | spec: 60 | serviceAccount: spring-cloud-kubernetes-configuration-watcher 61 | containers: 62 | - name: gateway 63 | image: springone/gateway 64 | imagePullPolicy: IfNotPresent 65 | readinessProbe: 66 | httpGet: 67 | port: 8070 68 | path: /actuator/health/readiness 69 | livenessProbe: 70 | httpGet: 71 | port: 8070 72 | path: /actuator/health/liveness 73 | ports: 74 | - containerPort: 8070 75 | -------------------------------------------------------------------------------- /gateway/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | shopping-parent 7 | com.example.chaos.monkey.shopping 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | gateway 13 | gateway 14 | 15 | 16 | 17 | org.springframework.cloud 18 | spring-cloud-starter-gateway 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-starter-circuitbreaker-reactor-resilience4j 23 | 24 | 25 | org.springframework.cloud 26 | spring-cloud-starter-loadbalancer 27 | 28 | 29 | org.springframework.cloud 30 | spring-cloud-starter-netflix-eureka-client 31 | 32 | 33 | org.springframework.cloud 34 | spring-cloud-starter-kubernetes-discoveryclient 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-actuator 39 | 40 | 41 | com.example.chaos.monkey.shopping 42 | shared 43 | ${parent.version} 44 | 45 | 46 | org.springframework.cloud 47 | spring-cloud-config-client 48 | 49 | 50 | 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-maven-plugin 55 | 56 | 57 | ${env.IMAGE} 58 | 59 | build-image 60 | 61 | 62 | 63 | package 64 | 65 | build-image 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /gateway/skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v2alpha3 2 | kind: Config 3 | metadata: 4 | name: gateway 5 | build: 6 | # artifacts: 7 | # - image: ryanjbaxter/fashion-bestseller 8 | # tagPolicy: 9 | # envTemplate: 10 | # template: "{{.IMAGE_NAME}}:{{.DIGEST_HEX}}" 11 | artifacts: 12 | - image: springone/gateway 13 | custom: 14 | buildCommand: "../mvnw clean install" 15 | dependencies: 16 | paths: 17 | - src 18 | - pom.xml 19 | # local: 20 | 21 | # jib: { 22 | # args: ["-Pjib"] 23 | # } 24 | deploy: 25 | kubectl: 26 | manifests: 27 | - k8s/deployment.yaml 28 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/example/chaos/monkey/shopping/gateway/GatewayApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.gateway; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | 6 | import com.example.chaos.monkey.shopping.domain.Product; 7 | 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.boot.autoconfigure.SpringBootApplication; 10 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 11 | import org.springframework.cloud.gateway.route.RouteLocator; 12 | import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.http.HttpHeaders; 15 | import org.springframework.http.ResponseEntity; 16 | import org.springframework.web.bind.annotation.GetMapping; 17 | import org.springframework.web.bind.annotation.RestController; 18 | 19 | @SpringBootApplication 20 | @RestController 21 | @EnableDiscoveryClient 22 | public class GatewayApplication { 23 | 24 | public static void main(String[] args) { 25 | SpringApplication.run(GatewayApplication.class, args); 26 | } 27 | 28 | @Bean 29 | public RouteLocator routes(RouteLocatorBuilder builder) { 30 | return builder.routes() 31 | .route(p -> p.path("/hotdeals**").filters( f -> 32 | f.circuitBreaker(c -> c.setName("hotdeals").setFallbackUri("forward:/fallback"))).uri("lb://hot-deals")) 33 | .route(p -> p.path("/fashion/**").filters(f -> 34 | f.circuitBreaker(c -> c.setName("fashion").setFallbackUri("forward:/fallback"))).uri("lb://fashion-bestseller")) 35 | .route(p -> p.path("/toys/**").filters(f -> 36 | f.circuitBreaker(c -> c.setName("toys").setFallbackUri("forward:/fallback"))).uri("lb://toys-bestseller")) 37 | .build(); 38 | } 39 | 40 | @GetMapping("/fallback") 41 | public ResponseEntity> fallback() { 42 | System.out.println("fallback enabled"); 43 | HttpHeaders headers = new HttpHeaders(); 44 | headers.add("fallback", "true"); 45 | return ResponseEntity.ok().headers(headers).body(Collections.emptyList()); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/example/chaos/monkey/shopping/gateway/StartPageController.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.gateway; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | import java.util.Collections; 6 | import java.util.function.Function; 7 | import org.springframework.boot.web.context.WebServerInitializedEvent; 8 | import org.springframework.context.ApplicationListener; 9 | import org.springframework.core.ParameterizedTypeReference; 10 | import org.springframework.http.HttpHeaders; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | import org.springframework.web.reactive.function.client.ClientResponse; 14 | import org.springframework.web.reactive.function.client.WebClient; 15 | import com.example.chaos.monkey.shopping.domain.Product; 16 | import com.example.chaos.monkey.shopping.gateway.domain.ProductResponse; 17 | import com.example.chaos.monkey.shopping.gateway.domain.ResponseType; 18 | import com.example.chaos.monkey.shopping.gateway.domain.Startpage; 19 | 20 | /** 21 | * @author Ryan Baxter 22 | */ 23 | @RestController 24 | public class StartPageController implements ApplicationListener { 25 | 26 | private ParameterizedTypeReference productParameterizedTypeReference = 27 | new ParameterizedTypeReference() {}; 28 | 29 | private Function> responseProcessor = clientResponse -> { 30 | HttpHeaders headers = clientResponse.headers().asHttpHeaders(); 31 | if(headers.containsKey("fallback") && headers.get("fallback").contains("true")) { 32 | return Mono.just(new ProductResponse(ResponseType.FALLBACK, Collections.emptyList())); 33 | } 34 | return clientResponse.bodyToFlux(productParameterizedTypeReference).collectList() 35 | .flatMap(products -> Mono.just(new ProductResponse(ResponseType.REMOTE_SERVICE, products))); 36 | }; 37 | private WebClient client; 38 | private ProductResponse errorResponse; 39 | 40 | public StartPageController() { 41 | 42 | this.errorResponse = new ProductResponse(); 43 | errorResponse.setResponseType(ResponseType.ERROR); 44 | errorResponse.setProducts(Collections.emptyList()); 45 | } 46 | 47 | 48 | @GetMapping("/startpage") 49 | public Mono getStartpage() { 50 | long start = System.currentTimeMillis(); 51 | Mono hotdeals = client.get().uri("/hotdeals").exchange().flatMap(responseProcessor) 52 | .onErrorResume(t -> { 53 | t.printStackTrace(); 54 | return Mono.just(errorResponse); 55 | }); 56 | Mono fashionBestSellers = client.get().uri("/fashion/bestseller").exchange().flatMap(responseProcessor) 57 | .onErrorResume(t -> { 58 | t.printStackTrace(); 59 | return Mono.just(errorResponse); 60 | }); 61 | Mono toysBestSellers = client.get().uri("/toys/bestseller").exchange().flatMap(responseProcessor) 62 | .onErrorResume(t -> { 63 | t.printStackTrace(); 64 | return Mono.just(errorResponse); 65 | }); 66 | 67 | Mono page = Mono.zip(hotdeals, fashionBestSellers, toysBestSellers).flatMap(t -> { 68 | Startpage p = new Startpage(); 69 | ProductResponse deals = t.getT1(); 70 | ProductResponse fashion = t.getT2(); 71 | ProductResponse toys = t.getT3(); 72 | p.setFashionResponse(fashion); 73 | p.setHotDealsResponse(deals); 74 | p.setToysResponse(toys); 75 | p.setStatusFashion(fashion.getResponseType().name()); 76 | p.setStatusHotDeals(deals.getResponseType().name()); 77 | p.setStatusToys(toys.getResponseType().name()); 78 | // Request duration 79 | p.setDuration(System.currentTimeMillis() - start); 80 | return Mono.just(p); 81 | }); 82 | 83 | return page; 84 | } 85 | 86 | @Override 87 | public void onApplicationEvent(WebServerInitializedEvent event) { 88 | this.client = WebClient.builder().baseUrl("http://localhost:" + event.getWebServer().getPort()).build(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/example/chaos/monkey/shopping/gateway/domain/ProductResponse.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.gateway.domain; 2 | 3 | import java.util.List; 4 | import com.example.chaos.monkey.shopping.domain.Product; 5 | 6 | /** 7 | * @author Benjamin Wilms 8 | */ 9 | public class ProductResponse { 10 | 11 | private ResponseType responseType; 12 | private List products; 13 | 14 | public ProductResponse(ResponseType responseType, List products) { 15 | this.responseType = responseType; 16 | this.products = products; 17 | } 18 | 19 | public ProductResponse() {} 20 | 21 | public void setResponseType(ResponseType responseType) { 22 | this.responseType = responseType; 23 | } 24 | 25 | public void setProducts(List products) { 26 | this.products = products; 27 | } 28 | 29 | public ResponseType getResponseType() { 30 | return responseType; 31 | } 32 | 33 | public List getProducts() { 34 | return products; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/example/chaos/monkey/shopping/gateway/domain/ResponseType.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.gateway.domain; 2 | 3 | /** 4 | * @author Benjamin Wilms 5 | */ 6 | public enum ResponseType { 7 | 8 | REMOTE_SERVICE, SECOND_TRY, FALLBACK, ERROR; 9 | } 10 | -------------------------------------------------------------------------------- /gateway/src/main/java/com/example/chaos/monkey/shopping/gateway/domain/Startpage.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.gateway.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonPropertyOrder; 4 | 5 | /** 6 | * @author Benjamin Wilms 7 | */ 8 | @JsonPropertyOrder({ "fashionResponse", "toysResponse", "hotDealsResponse", "duration", "statusFashion","statusToys","statusHotDeals" }) 9 | public class Startpage { 10 | 11 | private long duration; 12 | private String statusFashion; 13 | private String statusToys; 14 | private String statusHotDeals; 15 | private ProductResponse fashionResponse; 16 | private ProductResponse toysResponse; 17 | private ProductResponse hotDealsResponse; 18 | 19 | public void setFashionResponse(ProductResponse fashionResponse) { 20 | this.setStatusFashion(fashionResponse.getResponseType().name()); 21 | this.fashionResponse = fashionResponse; 22 | } 23 | 24 | public void setToysResponse(ProductResponse toysResponse) { 25 | this.setStatusToys(toysResponse.getResponseType().name()); 26 | this.toysResponse = toysResponse; 27 | } 28 | 29 | public void setHotDealsResponse(ProductResponse hotDealsResponse) { 30 | this.setStatusHotDeals(hotDealsResponse.getResponseType().name()); 31 | this.hotDealsResponse = hotDealsResponse; 32 | } 33 | 34 | public void setStatusFashion(String status) { 35 | this.statusFashion = status; 36 | } 37 | 38 | public void setStatusToys(String statusToys) { 39 | this.statusToys = statusToys; 40 | } 41 | 42 | public void setStatusHotDeals(String status) { 43 | this.statusHotDeals = status; 44 | } 45 | 46 | public void setDuration(long duration) { 47 | this.duration = duration; 48 | } 49 | 50 | public String getStatusFashion() { 51 | return statusFashion; 52 | } 53 | 54 | public String getStatusToys() { 55 | return statusToys; 56 | } 57 | 58 | public String getStatusHotDeals() { 59 | return statusHotDeals; 60 | } 61 | 62 | public ProductResponse getFashionResponse() { 63 | return fashionResponse; 64 | } 65 | 66 | public ProductResponse getToysResponse() { 67 | return toysResponse; 68 | } 69 | 70 | public ProductResponse getHotDealsResponse() { 71 | return hotDealsResponse; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /gateway/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8070 3 | spring: 4 | config: 5 | import: configserver:http://localhost:8888 6 | application: 7 | name: gateway 8 | cloud: 9 | gateway: 10 | discovery: 11 | locator: 12 | lowerCaseServiceId: true 13 | enabled: true 14 | --- 15 | spring: 16 | config: 17 | activate: 18 | on-cloud-platform: kubernetes 19 | import: configserver:http://spring-cloud-kubernetes-configserver:8888 20 | -------------------------------------------------------------------------------- /hot-deals/k8s/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: Service 7 | metadata: 8 | labels: 9 | app: hot-deals 10 | name: hot-deals 11 | spec: 12 | ports: 13 | - name: http 14 | port: 8083 15 | targetPort: 8083 16 | selector: 17 | app: hot-deals 18 | type: ClusterIP 19 | - apiVersion: v1 20 | kind: ServiceAccount 21 | metadata: 22 | labels: 23 | app: spring-cloud-kubernetes-configuration-watcher 24 | name: spring-cloud-kubernetes-configuration-watcher 25 | - apiVersion: rbac.authorization.k8s.io/v1 26 | kind: RoleBinding 27 | metadata: 28 | labels: 29 | app: spring-cloud-kubernetes-configuration-watcher 30 | name: spring-cloud-kubernetes-configuration-watcher:view 31 | roleRef: 32 | kind: Role 33 | apiGroup: rbac.authorization.k8s.io 34 | name: namespace-reader 35 | subjects: 36 | - kind: ServiceAccount 37 | name: spring-cloud-kubernetes-configuration-watcher 38 | - apiVersion: rbac.authorization.k8s.io/v1 39 | kind: Role 40 | metadata: 41 | namespace: default 42 | name: namespace-reader 43 | rules: 44 | - apiGroups: ["", "extensions", "apps"] 45 | resources: ["configmaps", "pods", "services", "endpoints", "secrets"] 46 | verbs: ["get", "list", "watch"] 47 | - apiVersion: apps/v1 48 | kind: Deployment 49 | metadata: 50 | name: hot-deals-deployment 51 | spec: 52 | selector: 53 | matchLabels: 54 | app: hot-deals 55 | template: 56 | metadata: 57 | labels: 58 | app: hot-deals 59 | spec: 60 | serviceAccount: spring-cloud-kubernetes-configuration-watcher 61 | containers: 62 | - name: hot-deals 63 | image: springone/hot-deals 64 | imagePullPolicy: IfNotPresent 65 | readinessProbe: 66 | httpGet: 67 | port: 8083 68 | path: /actuator/health/readiness 69 | livenessProbe: 70 | httpGet: 71 | port: 8083 72 | path: /actuator/health/liveness 73 | ports: 74 | - containerPort: 8083 75 | -------------------------------------------------------------------------------- /hot-deals/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | shopping-parent 7 | com.example.chaos.monkey.shopping 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | hot-deals 13 | hot-deals 14 | 15 | 16 | 17 | de.codecentric 18 | chaos-monkey-spring-boot 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-actuator 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | org.springframework.cloud 30 | spring-cloud-starter-netflix-eureka-client 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-devtools 35 | runtime 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-test 40 | test 41 | 42 | 43 | com.example.chaos.monkey.shopping 44 | shared 45 | ${parent.version} 46 | 47 | 48 | org.springframework.cloud 49 | spring-cloud-config-client 50 | 51 | 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-maven-plugin 57 | 58 | 59 | ${env.IMAGE} 60 | 61 | build-image 62 | 63 | 64 | 65 | package 66 | 67 | build-image 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /hot-deals/skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v2alpha3 2 | kind: Config 3 | metadata: 4 | name: fashion-bestseller 5 | build: 6 | # artifacts: 7 | # - image: ryanjbaxter/fashion-bestseller 8 | # tagPolicy: 9 | # envTemplate: 10 | # template: "{{.IMAGE_NAME}}:{{.DIGEST_HEX}}" 11 | artifacts: 12 | - image: springone/hot-deals 13 | custom: 14 | buildCommand: "../mvnw clean install" 15 | dependencies: 16 | paths: 17 | - src 18 | - pom.xml 19 | # local: 20 | 21 | # jib: { 22 | # args: ["-Pjib"] 23 | # } 24 | deploy: 25 | kubectl: 26 | manifests: 27 | - k8s/deployment.yaml 28 | -------------------------------------------------------------------------------- /hot-deals/src/main/java/com/example/chaos/monkey/shopping/hotdeals/HotDealsApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.hotdeals; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | 7 | @SpringBootApplication 8 | @EnableDiscoveryClient 9 | public class HotDealsApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(HotDealsApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /hot-deals/src/main/java/com/example/chaos/monkey/shopping/hotdeals/HotDealsRestController.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.hotdeals; 2 | 3 | 4 | import com.example.chaos.monkey.shopping.domain.Product; 5 | import com.example.chaos.monkey.shopping.domain.ProductBuilder; 6 | import com.example.chaos.monkey.shopping.domain.ProductCategory; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | import java.util.concurrent.atomic.AtomicLong; 14 | 15 | /** 16 | * @author Benjamin Wilms 17 | */ 18 | @RestController 19 | public class HotDealsRestController { 20 | 21 | @GetMapping("/hotdeals") 22 | public List getHotDeals() { 23 | AtomicLong aLong = new AtomicLong(7); 24 | 25 | ProductBuilder productBuilder = new ProductBuilder(); 26 | 27 | Product product1 = productBuilder.setCategory(ProductCategory.FASHION).setId(aLong.getAndIncrement()).setName("Thermal Winter Warm Hot Heat" + 28 | " Socks") 29 | .createProduct(); 30 | 31 | Product product2 = productBuilder.setCategory(ProductCategory.TOYS).setId(aLong.getAndIncrement()).setName("RC Quadcopter Drone with 2.0MP Camera Live") 32 | .createProduct(); 33 | 34 | Product product3 = productBuilder.setCategory(ProductCategory.BOOKS).setId(aLong.getAndIncrement()).setName("Spring Boot 2: Moderne Softwareentwicklung mit Spring 5") 35 | .createProduct(); 36 | return Arrays.asList(product1, product2, product3); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /hot-deals/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8083 3 | 4 | spring: 5 | application: 6 | name: hot-deals 7 | config: 8 | import: configserver:http://localhost:8888 9 | 10 | --- 11 | spring: 12 | config: 13 | activate: 14 | on-cloud-platform: kubernetes 15 | import: configserver:http://spring-cloud-kubernetes-configserver:8888 -------------------------------------------------------------------------------- /hot-deals/src/test/java/com/example/chaos/monkey/shopping/hotdeals/HotDealsApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.hotdeals; 2 | 3 | 4 | import org.junit.jupiter.api.Test; 5 | 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | 8 | /** 9 | * @author Benjamin Wilms 10 | */ 11 | @SpringBootTest 12 | public class HotDealsApplicationTest { 13 | @Test 14 | public void contextLoads() { 15 | } 16 | } -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | ########################################################################################## 204 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 205 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 206 | ########################################################################################## 207 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 208 | if [ "$MVNW_VERBOSE" = true ]; then 209 | echo "Found .mvn/wrapper/maven-wrapper.jar" 210 | fi 211 | else 212 | if [ "$MVNW_VERBOSE" = true ]; then 213 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 214 | fi 215 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 216 | while IFS="=" read key value; do 217 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 218 | esac 219 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 220 | if [ "$MVNW_VERBOSE" = true ]; then 221 | echo "Downloading from: $jarUrl" 222 | fi 223 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 224 | 225 | if command -v wget > /dev/null; then 226 | if [ "$MVNW_VERBOSE" = true ]; then 227 | echo "Found wget ... using wget" 228 | fi 229 | wget "$jarUrl" -O "$wrapperJarPath" 230 | elif command -v curl > /dev/null; then 231 | if [ "$MVNW_VERBOSE" = true ]; then 232 | echo "Found curl ... using curl" 233 | fi 234 | curl -o "$wrapperJarPath" "$jarUrl" 235 | else 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Falling back to using Java to download" 238 | fi 239 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 240 | if [ -e "$javaClass" ]; then 241 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 242 | if [ "$MVNW_VERBOSE" = true ]; then 243 | echo " - Compiling MavenWrapperDownloader.java ..." 244 | fi 245 | # Compiling the Java class 246 | ("$JAVA_HOME/bin/javac" "$javaClass") 247 | fi 248 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 249 | # Running the downloader 250 | if [ "$MVNW_VERBOSE" = true ]; then 251 | echo " - Running MavenWrapperDownloader.java ..." 252 | fi 253 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 254 | fi 255 | fi 256 | fi 257 | fi 258 | ########################################################################################## 259 | # End of extension 260 | ########################################################################################## 261 | 262 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 263 | if [ "$MVNW_VERBOSE" = true ]; then 264 | echo $MAVEN_PROJECTBASEDIR 265 | fi 266 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 267 | 268 | # For Cygwin, switch paths to Windows format before running java 269 | if $cygwin; then 270 | [ -n "$M2_HOME" ] && 271 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 272 | [ -n "$JAVA_HOME" ] && 273 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 274 | [ -n "$CLASSPATH" ] && 275 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 276 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 277 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 278 | fi 279 | 280 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 281 | 282 | exec "$JAVACMD" \ 283 | $MAVEN_OPTS \ 284 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 285 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 286 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 287 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( 125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | echo Found %WRAPPER_JAR% 132 | ) else ( 133 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 134 | echo Downloading from: %DOWNLOAD_URL% 135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" 136 | echo Finished downloading %WRAPPER_JAR% 137 | ) 138 | @REM End of extension 139 | 140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 141 | if ERRORLEVEL 1 goto error 142 | goto end 143 | 144 | :error 145 | set ERROR_CODE=1 146 | 147 | :end 148 | @endlocal & set ERROR_CODE=%ERROR_CODE% 149 | 150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 154 | :skipRcPost 155 | 156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 158 | 159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 160 | 161 | exit /B %ERROR_CODE% 162 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.example.chaos.monkey.shopping 8 | shopping-parent 9 | 1.0-SNAPSHOT 10 | 11 | hot-deals 12 | fashion-bestseller 13 | toys-bestseller 14 | gateway 15 | shared 16 | configserver 17 | discoveryserver 18 | 19 | pom 20 | 21 | 22 | 23 | 24 | ryanjbaxter 25 | 0.31.0 26 | 27 | UTF-8 28 | UTF-8 29 | 1.8 30 | ${java.version} 31 | ${java.version} 32 | 2.5.3 33 | 2020.0.4-SNAPSHOT 34 | 1.18.10 35 | 2.3.0 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-dependencies 44 | ${spring-boot-dependencies.version} 45 | pom 46 | import 47 | 48 | 49 | org.springframework.cloud 50 | spring-cloud-dependencies 51 | ${spring-cloud.version} 52 | pom 53 | import 54 | 55 | 56 | de.codecentric 57 | chaos-monkey-spring-boot 58 | ${chaos-monkey-spring-boot.version} 59 | 60 | 61 | com.netflix.hystrix 62 | hystrix-core 63 | 1.5.12 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-starter-actuator 68 | ${spring-boot-dependencies.version} 69 | 70 | 71 | org.springframework.boot 72 | spring-boot-starter-web 73 | ${spring-boot-dependencies.version} 74 | 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-devtools 79 | ${spring-boot-dependencies.version} 80 | runtime 81 | 82 | 83 | org.springframework.boot 84 | spring-boot-starter-test 85 | ${spring-boot-dependencies.version} 86 | test 87 | 88 | 89 | org.projectlombok 90 | lombok 91 | ${lombok.version} 92 | provided 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | org.springframework.boot 102 | spring-boot-maven-plugin 103 | 104 | 105 | 106 | repackage 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | imagename 118 | 119 | 120 | !env.IMAGE 121 | 122 | 123 | 124 | ${project.artifactId}:${project.version} 125 | 126 | 127 | 128 | docker 129 | 130 | 131 | 132 | 133 | io.fabric8 134 | docker-maven-plugin 135 | ${docker-maven-plugin.version} 136 | 137 | 138 | 139 | ${docker.image.prefix}/${project.artifactId} 140 | 141 | ${project.basedir} 142 | 143 | ${project.version} 144 | 145 | 146 | target/${project.build.finalName}.jar 147 | ${project.name} 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | build-image 156 | package 157 | 158 | build 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | spring-snapshots 172 | Spring Snapshots 173 | https://repo.spring.io/libs-snapshot-local 174 | 175 | true 176 | 177 | 178 | 179 | spring-milestones 180 | Spring Milestones 181 | https://repo.spring.io/libs-milestone-local 182 | 183 | false 184 | 185 | 186 | 187 | spring-releases 188 | Spring Releases 189 | https://repo.spring.io/release 190 | 191 | false 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /shared/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | shopping-parent 7 | com.example.chaos.monkey.shopping 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | shared 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /shared/src/main/java/com/example/chaos/monkey/shopping/domain/Product.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.domain; 2 | 3 | /** 4 | * @author Benjamin Wilms 5 | */ 6 | public class Product { 7 | 8 | private long id; 9 | private String name; 10 | private ProductCategory category; 11 | 12 | public Product() {} 13 | 14 | public Product(long id, String name, ProductCategory category) { 15 | this.id = id; 16 | this.name = name; 17 | this.category = category; 18 | } 19 | 20 | public long getId() { 21 | return id; 22 | } 23 | 24 | public void setId(long id) { 25 | this.id = id; 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | public void setName(String name) { 33 | this.name = name; 34 | } 35 | 36 | public ProductCategory getCategory() { 37 | return category; 38 | } 39 | 40 | public void setCategory(ProductCategory category) { 41 | this.category = category; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /shared/src/main/java/com/example/chaos/monkey/shopping/domain/ProductBuilder.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.domain; 2 | 3 | public class ProductBuilder { 4 | private long id; 5 | private String name; 6 | private ProductCategory category; 7 | 8 | public ProductBuilder setId(long id) { 9 | this.id = id; 10 | return this; 11 | } 12 | 13 | public ProductBuilder setName(String name) { 14 | this.name = name; 15 | return this; 16 | } 17 | 18 | public ProductBuilder setCategory(ProductCategory category) { 19 | this.category = category; 20 | return this; 21 | } 22 | 23 | public Product createProduct() { 24 | return new Product(id, name, category); 25 | } 26 | } -------------------------------------------------------------------------------- /shared/src/main/java/com/example/chaos/monkey/shopping/domain/ProductCategory.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.domain; 2 | 3 | /** 4 | * @author Benjamin Wilms 5 | */ 6 | public enum ProductCategory { 7 | FASHION,TOYS,BOOKS; 8 | } 9 | -------------------------------------------------------------------------------- /toys-bestseller/configmap.yml: -------------------------------------------------------------------------------- 1 | kind: ConfigMap 2 | apiVersion: v1 3 | metadata: 4 | name: toys-bestseller 5 | labels: 6 | "spring.cloud.kubernetes.config": "true" 7 | data: 8 | chaos.monkey.enabled: "false" -------------------------------------------------------------------------------- /toys-bestseller/k8s/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: List 4 | items: 5 | - apiVersion: v1 6 | kind: Service 7 | metadata: 8 | labels: 9 | app: toys-bestseller 10 | name: toys-bestseller 11 | spec: 12 | ports: 13 | - name: http 14 | port: 8081 15 | targetPort: 8081 16 | selector: 17 | app: toys-bestseller 18 | type: ClusterIP 19 | - apiVersion: v1 20 | kind: ServiceAccount 21 | metadata: 22 | labels: 23 | app: spring-cloud-kubernetes-configuration-watcher 24 | name: spring-cloud-kubernetes-configuration-watcher 25 | - apiVersion: rbac.authorization.k8s.io/v1 26 | kind: RoleBinding 27 | metadata: 28 | labels: 29 | app: spring-cloud-kubernetes-configuration-watcher 30 | name: spring-cloud-kubernetes-configuration-watcher:view 31 | roleRef: 32 | kind: Role 33 | apiGroup: rbac.authorization.k8s.io 34 | name: namespace-reader 35 | subjects: 36 | - kind: ServiceAccount 37 | name: spring-cloud-kubernetes-configuration-watcher 38 | - apiVersion: rbac.authorization.k8s.io/v1 39 | kind: Role 40 | metadata: 41 | namespace: default 42 | name: namespace-reader 43 | rules: 44 | - apiGroups: ["", "extensions", "apps"] 45 | resources: ["configmaps", "pods", "services", "endpoints", "secrets"] 46 | verbs: ["get", "list", "watch"] 47 | - apiVersion: apps/v1 48 | kind: Deployment 49 | metadata: 50 | name: toys-bestseller-deployment 51 | spec: 52 | selector: 53 | matchLabels: 54 | app: toys-bestseller 55 | template: 56 | metadata: 57 | labels: 58 | app: toys-bestseller 59 | spec: 60 | serviceAccount: spring-cloud-kubernetes-configuration-watcher 61 | containers: 62 | - name: toys-bestseller 63 | image: springone/toys-bestseller 64 | imagePullPolicy: IfNotPresent 65 | readinessProbe: 66 | httpGet: 67 | port: 8081 68 | path: /actuator/health/readiness 69 | livenessProbe: 70 | httpGet: 71 | port: 8081 72 | path: /actuator/health/liveness 73 | ports: 74 | - containerPort: 8081 75 | -------------------------------------------------------------------------------- /toys-bestseller/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | shopping-parent 7 | com.example.chaos.monkey.shopping 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | toys-bestseller 13 | toys 14 | 15 | 16 | de.codecentric 17 | chaos-monkey-spring-boot 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter-actuator 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-web 26 | 27 | 28 | org.springframework.cloud 29 | spring-cloud-starter-netflix-eureka-client 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-devtools 34 | runtime 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-test 39 | test 40 | 41 | 42 | com.example.chaos.monkey.shopping 43 | shared 44 | ${parent.version} 45 | 46 | 47 | org.springframework.cloud 48 | spring-cloud-config-client 49 | 50 | 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-maven-plugin 56 | 57 | 58 | ${env.IMAGE} 59 | 60 | build-image 61 | 62 | 63 | 64 | package 65 | 66 | build-image 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /toys-bestseller/skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v2alpha3 2 | kind: Config 3 | metadata: 4 | name: toys-bestseller 5 | build: 6 | # artifacts: 7 | # - image: ryanjbaxter/fashion-bestseller 8 | # tagPolicy: 9 | # envTemplate: 10 | # template: "{{.IMAGE_NAME}}:{{.DIGEST_HEX}}" 11 | artifacts: 12 | - image: springone/toys-bestseller 13 | custom: 14 | buildCommand: "../mvnw clean install" 15 | dependencies: 16 | paths: 17 | - src 18 | - pom.xml 19 | # local: 20 | 21 | # jib: { 22 | # args: ["-Pjib"] 23 | # } 24 | deploy: 25 | kubectl: 26 | manifests: 27 | - k8s/deployment.yaml 28 | -------------------------------------------------------------------------------- /toys-bestseller/src/main/java/com/example/chaos/monkey/shopping/bestseller/toys/BestsellerToysApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.bestseller.toys; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | 7 | @SpringBootApplication 8 | @EnableDiscoveryClient 9 | public class BestsellerToysApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(BestsellerToysApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /toys-bestseller/src/main/java/com/example/chaos/monkey/shopping/bestseller/toys/BestsellerToysRestController.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.bestseller.toys; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.UUID; 6 | import java.util.concurrent.atomic.AtomicLong; 7 | 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | import com.example.chaos.monkey.shopping.domain.Product; 11 | import com.example.chaos.monkey.shopping.domain.ProductBuilder; 12 | import com.example.chaos.monkey.shopping.domain.ProductCategory; 13 | 14 | import org.springframework.web.bind.annotation.GetMapping; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RestController; 17 | 18 | /** 19 | * @author Benjamin Wilms 20 | */ 21 | @RestController 22 | @RequestMapping("/toys") 23 | public class BestsellerToysRestController { 24 | 25 | private String appid = UUID.randomUUID().toString(); 26 | 27 | @GetMapping("/bestseller") 28 | public List getBestsellerProducts(HttpServletResponse response) { 29 | response.addHeader("appid", appid); 30 | AtomicLong aLong = new AtomicLong(1); 31 | 32 | ProductBuilder productBuilder = new ProductBuilder(); 33 | 34 | Product product1 = productBuilder.setCategory(ProductCategory.TOYS).setId(aLong.getAndIncrement()).setName("LEGO Star Wars Yodas Hut") 35 | .createProduct(); 36 | 37 | Product product2 = productBuilder.setCategory(ProductCategory.TOYS).setId(aLong.getAndIncrement()).setName("LEGO Star Wars Millennium Falcon") 38 | .createProduct(); 39 | 40 | Product product3 = productBuilder.setCategory(ProductCategory.TOYS).setId(aLong.getAndIncrement()).setName("LEGO Star Wars Imperial Tie Fighter") 41 | .createProduct(); 42 | return Arrays.asList(product1, product2, product3); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /toys-bestseller/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8081 3 | spring: 4 | profiles: 5 | active: chaos-monkey 6 | application: 7 | name: toys-bestseller 8 | config: 9 | import: configserver:http://localhost:8888 10 | --- 11 | spring: 12 | config: 13 | activate: 14 | on-cloud-platform: kubernetes 15 | import: configserver:http://spring-cloud-kubernetes-configserver:8888 -------------------------------------------------------------------------------- /toys-bestseller/src/test/java/com/example/chaos/monkey/shopping/bestseller/toys/BestsellerToysApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.example.chaos.monkey.shopping.bestseller.toys; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | /** 8 | * @author Benjamin Wilms 9 | */ 10 | @SpringBootTest 11 | public class BestsellerToysApplicationTest { 12 | 13 | @Test 14 | public void contextLoads() { 15 | } 16 | 17 | } --------------------------------------------------------------------------------