├── .gitignore ├── .travis.settings.xml ├── .travis.yml ├── README.md ├── catalog-go ├── .gitignore ├── Dockerfile ├── Makefile ├── catalog-go ├── handlers.go ├── main.go ├── product.go ├── routes.go ├── static │ └── images │ │ └── golang.png └── templates │ └── homepage.html ├── catalog-spring-boot ├── .gitignore ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── redhat │ │ └── cloudnative │ │ └── catalog │ │ └── CatalogApplication.java │ └── resources │ ├── application.properties │ ├── import.sql │ └── static │ ├── index.html │ └── spring-boot.png ├── gateway-vertx ├── .gitignore ├── openshift │ ├── istio-gateway.yml │ └── virtualservice.yml ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── redhat │ │ │ └── cloudnative │ │ │ └── gateway │ │ │ └── GatewayVerticle.java │ └── resources │ │ ├── assets │ │ ├── index.html │ │ └── vert.x_logo.png │ │ └── vertx-default-jul-logging.properties │ └── test │ └── resources │ └── config.json ├── inventory-thorntail ├── .gitignore ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── redhat │ │ └── cloudnative │ │ └── inventory │ │ └── InventoryApplication.java │ ├── resources │ ├── META-INF │ │ ├── load.sql │ │ └── persistence.xml │ └── project-defaults.yml │ └── webapp │ ├── WEB-INF │ └── beans.xml │ ├── index.html │ └── thorntail_icon.png ├── inventory-vertx ├── .gitignore ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── redhat │ │ └── cloudnative │ │ └── inventory │ │ └── InventoryVerticle.java │ └── resources │ ├── assets │ ├── index.html │ └── vert.x_logo.png │ ├── config │ └── app-config.yml │ └── vertx-default-jul-logging.properties ├── scripts └── runGatewayService.sh ├── solutions ├── all │ ├── catalog-spring-boot │ │ ├── .gitignore │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── com │ │ │ │ └── redhat │ │ │ │ └── cloudnative │ │ │ │ └── catalog │ │ │ │ ├── CatalogApplication.java │ │ │ │ ├── CatalogController.java │ │ │ │ ├── Product.java │ │ │ │ └── ProductRepository.java │ │ │ └── resources │ │ │ ├── application.properties │ │ │ └── import.sql │ ├── coolstore-template.yaml │ ├── gateway-vertx │ │ ├── .gitignore │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── com │ │ │ │ └── redhat │ │ │ │ └── cloudnative │ │ │ │ └── gateway │ │ │ │ └── GatewayVerticle.java │ │ │ └── resources │ │ │ └── vertx-default-jul-logging.properties │ ├── inventory-wildfly-swarm │ │ ├── .gitignore │ │ ├── Jenkinsfile │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── com │ │ │ │ └── redhat │ │ │ │ └── cloudnative │ │ │ │ └── inventory │ │ │ │ ├── Inventory.java │ │ │ │ ├── InventoryApplication.java │ │ │ │ └── InventoryResource.java │ │ │ └── resources │ │ │ ├── META-INF │ │ │ ├── load.sql │ │ │ └── persistence.xml │ │ │ └── project-defaults.yml │ └── kill-mvn-process.sh ├── catalog-spring-boot │ ├── CatalogController.java │ ├── Product.java │ ├── ProductRepository.java │ ├── solve&deploy.sh │ └── solve.sh ├── fault-tolerance-gateway-vertx │ └── GatewayVerticle.java ├── gateway-vertx │ ├── GatewayVerticle.java │ ├── solve&deploy.sh │ └── solve.sh ├── inventory-thorntail │ ├── Inventory.java │ ├── InventoryResource.java │ ├── pom.xml │ ├── solve&deploy.sh │ └── solve.sh ├── inventory-vertx │ ├── InventoryVerticle.java │ └── solve.sh ├── lab-10 │ ├── InventoryResource.java │ ├── solve&deploy.sh │ └── solve.sh ├── lab-5 │ └── solve.sh ├── lab-6 │ └── solve.sh ├── lab-7 │ ├── GatewayVerticle.java │ └── solve.sh ├── lab-8 │ └── solve.sh └── lab-9 │ └── solve.sh └── web-nodejs ├── .gitignore ├── app ├── app.js ├── controllers │ └── controllers.js ├── coolstore.config.js ├── css │ └── coolstore.css ├── directives │ └── header.js ├── imgs │ ├── 16 oz. Vortex Tumbler.jpg │ ├── Forge Laptop Sticker.jpg │ ├── Lytro Camera.jpg │ ├── Oculus Rift.jpg │ ├── Ogio Caliber Polo.jpg │ ├── Pebble Smart Watch.jpg │ ├── Red Fedora.jpg │ ├── Solid Performance Polo.jpg │ └── logo.png ├── keycloak.config.js ├── routes │ └── routes.js └── services │ ├── cart.js │ └── catalog.js ├── bower.json ├── package.json ├── server.js └── views ├── index.html └── partials ├── cart.html ├── header.html └── home.html /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.iml 3 | *.log 4 | *.class 5 | tmp/ 6 | .idea/ 7 | .openshiftio 8 | .settings/ 9 | .classpath 10 | .project 11 | .DS_Store 12 | bower_components/ 13 | node_modules/ 14 | .vscode/ -------------------------------------------------------------------------------- /.travis.settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | jboss-ga-repository 8 | 9 | 10 | jboss-ga-repository 11 | http://maven.repository.redhat.com/ga 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | jboss-ga-plugin-repository 23 | http://maven.repository.redhat.com/ga 24 | 25 | true 26 | 27 | 28 | false 29 | 30 | 31 | 32 | 33 | 34 | jboss-techpreview-repository 35 | 36 | 37 | jboss-techpreview-repository 38 | http://maven.repository.redhat.com/techpreview/all 39 | 40 | true 41 | 42 | 43 | false 44 | 45 | 46 | 47 | 48 | 49 | jboss-techpreview-plugin-repository 50 | http://maven.repository.redhat.com/techpreview/all 51 | 52 | true 53 | 54 | 55 | false 56 | 57 | 58 | 59 | 60 | 61 | jboss-earlyaccess-repository 62 | 63 | 64 | jboss-earlyaccess-repository 65 | http://maven.repository.redhat.com/earlyaccess/all/ 66 | 67 | true 68 | 69 | 70 | false 71 | 72 | 73 | 74 | 75 | 76 | jboss-earlyaccess-plugin-repository 77 | http://maven.repository.redhat.com/earlyaccess/all/ 78 | 79 | true 80 | 81 | 82 | false 83 | 84 | 85 | 86 | 87 | 88 | 89 | jboss-ga-repository 90 | jboss-techpreview-repository 91 | jboss-earlyaccess-repository 92 | 93 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - oraclejdk8 5 | 6 | before_install: 7 | - cp .travis.settings.xml $HOME/.m2/settings.xml 8 | - nvm install 4.4 9 | 10 | env: 11 | - BUILD_CMD="mvn -f catalog-spring-boot package" 12 | - BUILD_CMD="mvn -f gateway-vertx package" 13 | - BUILD_CMD="mvn -f inventory-wildfly-swarm package" 14 | - BUILD_CMD="npm install --prefix web-nodejs" 15 | - BUILD_CMD="mvn -f solutions/all/catalog-spring-boot package" 16 | - BUILD_CMD="mvn -f solutions/all/gateway-vertx package" 17 | - BUILD_CMD="mvn -f solutions/all/inventory-wildfly-swarm package" 18 | 19 | script: 20 | - $BUILD_CMD -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cloud Native Roadshow Labs [![Build Status](https://travis-ci.org/openshift-labs/cloud-native-labs.svg?branch=ocp-3.11)](https://travis-ci.org/openshift-labs/cloud-native-labs) 2 | 3 | This is a one-day hands-on lab experience for bulding Cloud Native applications using 4 | Red Hat OpenShift Application Runtimes (Spring Boot, WildFly Swarm, Eclipse Vert.x and Node.js) 5 | utilizing a microservices architecture. 6 | 7 | 8 | ## CoolStore Online Store App 9 | 10 | CoolStore is an online store web application built using Spring Boot, WildFly Swarm, Eclipse Vert.x, 11 | Node.js and AngularJS adopting the microservices architecture. 12 | 13 | * **Web**: A Node.js/Angular front-end 14 | * **API Gateway**: aggregates API calls to back-end services and provides a condenses REST API for front-end 15 | * **Catalog**: a REST API for the product catalog and product information 16 | * **Inventory**: a REST API for product's inventory status 17 | 18 | ``` 19 | +-------------+ 20 | | | 21 | | Web | 22 | | | 23 | | Node.js | 24 | | AngularJS | 25 | +------+------+ 26 | | 27 | v 28 | +------+------+ 29 | | | 30 | | API Gateway | 31 | | | 32 | | Vert.x | 33 | | | 34 | +------+------+ 35 | | 36 | +---------+---------+ 37 | v v 38 | +------+------+ +------+------+ 39 | | | | | 40 | | Catalog | | Inventory | 41 | | | | | 42 | | Spring Boot | |WildFly Swarm| 43 | | | | | 44 | +-------------+ +-------------+ 45 | ``` 46 | -------------------------------------------------------------------------------- /catalog-go/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.out -------------------------------------------------------------------------------- /catalog-go/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.12 as builder 2 | WORKDIR /go/src/app 3 | COPY . /go/src/app 4 | 5 | RUN go get -d -v ./... 6 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -gcflags "-N -l" -ldflags="-compressdwarf=false" -o catalog-go . 7 | 8 | FROM alpine:3.9 9 | COPY --from=builder /go/src/app /app 10 | WORKDIR /app 11 | EXPOSE 8080 12 | CMD ["/app/catalog-go"] -------------------------------------------------------------------------------- /catalog-go/Makefile: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: compile-local 3 | compile-local: 4 | CGO_ENABLED=0 go build -gcflags "-N -l" -ldflags="-compressdwarf=false" -o catalog-go.out . 5 | -------------------------------------------------------------------------------- /catalog-go/catalog-go: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/catalog-go/catalog-go -------------------------------------------------------------------------------- /catalog-go/handlers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "html/template" 6 | "log" 7 | "encoding/json" 8 | ) 9 | 10 | /** 11 | * Flag for throwing a 503 when enabled 12 | */ 13 | var misbehave = false 14 | 15 | func HomePage(w http.ResponseWriter, r *http.Request){ 16 | 17 | template := template.Must(template.ParseFiles("templates/homepage.html")) 18 | 19 | err := template.Execute(w, nil) //execute the template 20 | if err != nil { // if there is an error 21 | log.Print("template executing error: ", err) //log it 22 | http.Error(w, err.Error(), http.StatusInternalServerError) 23 | } 24 | } 25 | 26 | func GetProducts(w http.ResponseWriter, r *http.Request){ 27 | 28 | if misbehave { 29 | w.WriteHeader(http.StatusServiceUnavailable) 30 | w.Write([]byte("Misbehavior of the Catalog GoLang Service\n")) 31 | } else { 32 | products := Products{ 33 | Product{ ItemId: "329299", Name: "Red Fedora", Description: "OFFICIAL RED HAT FEDORA", Price: 34.99}, 34 | Product{ ItemId: "329199", Name: "Forge Laptop Sticker", Description: "JBOSS COMMUNITY FORGE PROJECT STICKER", Price: 8.50}, 35 | Product{ ItemId: "165613", Name: "Solid Performance Polo", Description: "MOISTURE-WICKING, ANTIMICROBIAL 100% POLYESTER DESIGN WICKS FOR LIFE OF GARMENT. NO-CURL, RIB-KNIT COLLAR...", Price: 17.80}, 36 | Product{ ItemId: "165614", Name: "Ogio Caliber Polo", Description: "MOISTURE-WICKING 100% POLYESTER. RIB-KNIT COLLAR AND CUFFS; OGIO JACQUARD TAPE INSITEM_IDE NECK; BAR-TACKED THREE-BUTTON PLACKET WITH...", Price: 28.75}, 37 | Product{ ItemId: "165954", Name: "16 oz. Vortex Tumbler", Description: "DOUBLE-WALL INSULATED, BPA-FREE, ACRYLIC CUP. PUSH-ON LITEM_ID WITH THUMB-SLITEM_IDE CLOSURE; FOR HOT AND COLD BEVERAGES. HOLDS 16 OZ. HAND WASH ONLY. IMPRINT. CLEAR.", Price: 6.00}, 38 | Product{ ItemId: "444434", Name: "Pebble Smart Watch", Description: "SMART GLASSES AND SMART WATCHES ARE PERHAPS TWO OF THE MOST EXCITING DEVELOPMENTS IN RECENT YEARS.", Price: 24.00}, 39 | Product{ ItemId: "444435", Name: "Oculus Rift", Description: "THE WORLD OF GAMING HAS ALSO UNDERGONE SOME VERY UNIQUE AND COMPELLING TECH ADVANCES IN RECENT YEARS. VIRTUAL REALITY...", Price: 106.00}, 40 | Product{ ItemId: "444436", Name: "Lytro Camera", Description: "CONSUMERS WHO WANT TO UP THEIR PHOTOGRAPHY GAME ARE LOOKING AT NEWFANGLED CAMERAS LIKE THE LYTRO FIELD CAMERA, DESIGNED TO ...", Price: 44.30}, 41 | } 42 | 43 | // Define Content-Type = application/json 44 | w.Header().Set("Content-Type", "application/json; charset=UTF-8") 45 | if err := json.NewEncoder(w).Encode(products); err != nil { 46 | panic(err) 47 | } 48 | } 49 | } 50 | 51 | func Behave(w http.ResponseWriter, r *http.Request){ 52 | misbehave = false 53 | log.Print("'misbehave' has been set to 'false'") 54 | w.WriteHeader(http.StatusOK) 55 | w.Write([]byte("Next request to / will return 200\n")) 56 | return 57 | } 58 | 59 | func Misbehave(w http.ResponseWriter, r *http.Request){ 60 | misbehave = true 61 | log.Print("'misbehave' has been set to 'true'") 62 | w.WriteHeader(http.StatusOK) 63 | w.Write([]byte("Next request to / will return a 503\n")) 64 | return 65 | } -------------------------------------------------------------------------------- /catalog-go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | 10 | router := NewRouter() 11 | 12 | log.Println("Listening...") 13 | log.Fatal(http.ListenAndServe(":8080", router)) 14 | } -------------------------------------------------------------------------------- /catalog-go/product.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Product struct { 4 | ItemId string `json:"itemId"` 5 | Name string `json:"name"` 6 | Description string `json:"description"` 7 | Price float32 `json:"price"` 8 | } 9 | 10 | type Products []Product -------------------------------------------------------------------------------- /catalog-go/routes.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "github.com/gorilla/mux" 6 | ) 7 | 8 | type Route struct { 9 | Name string 10 | Method string 11 | Pattern string 12 | HandlerFunc http.HandlerFunc 13 | } 14 | 15 | type Routes []Route 16 | 17 | func NewRouter() *mux.Router { 18 | router := mux.NewRouter().StrictSlash(true) 19 | router. 20 | PathPrefix("/static/"). 21 | Handler(http.StripPrefix("/static/", 22 | http.FileServer(http.Dir("static")))) 23 | 24 | for _, route := range routes { 25 | router. 26 | Methods(route.Method). 27 | Path(route.Pattern). 28 | Name(route.Name). 29 | Handler(route.HandlerFunc) 30 | } 31 | 32 | return router 33 | } 34 | 35 | var routes = Routes{ 36 | Route{ 37 | "HomePage", 38 | "GET", 39 | "/", 40 | HomePage, 41 | }, 42 | Route{ 43 | "GetProducts", 44 | "GET", 45 | "/api/catalog", 46 | GetProducts, 47 | }, 48 | Route{ 49 | "Misbehave", 50 | "GET", 51 | "/api/misbehave", 52 | Misbehave, 53 | }, 54 | Route{ 55 | "Behave", 56 | "GET", 57 | "/api/behave", 58 | Behave, 59 | }, 60 | } -------------------------------------------------------------------------------- /catalog-go/static/images/golang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/catalog-go/static/images/golang.png -------------------------------------------------------------------------------- /catalog-go/templates/homepage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Catalog Service 6 | 8 | 10 | 12 | 13 | 14 |
15 |
16 |

Golang Catalog Service

17 |

This is a Golang Microservice for the CoolStore Demo. (Test it) 18 |

19 |
20 |
21 |
22 | 25 |
26 | 27 | -------------------------------------------------------------------------------- /catalog-spring-boot/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.iml 3 | *.log 4 | *.class 5 | tmp/ 6 | .idea/ 7 | .openshiftio 8 | .settings/ 9 | .classpath 10 | .project 11 | .DS_Store 12 | bower_components/ 13 | node_modules/ -------------------------------------------------------------------------------- /catalog-spring-boot/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.redhat.cloudnative 7 | catalog 8 | 1.0-SNAPSHOT 9 | CoolStore Catalog Service 10 | CoolStore Catalog Service with Spring Boot 11 | 12 | 1.5.14.RELEASE 13 | 1.5.14.Final-redhat-1 14 | 0.2.0.RELEASE 15 | 3.5.40 16 | 42.2.4 17 | 18 | 19 | 20 | redhat-ga 21 | https://maven.repository.redhat.com/ga/ 22 | 23 | 24 | 25 | 26 | redhat-ga-plugins 27 | https://maven.repository.redhat.com/ga/ 28 | 29 | 30 | 31 | 32 | 33 | me.snowdrop 34 | spring-boot-bom 35 | ${spring-boot.bom.version} 36 | pom 37 | import 38 | 39 | 40 | org.springframework.cloud 41 | spring-cloud-kubernetes-dependencies 42 | ${spring.k8s.bom.version} 43 | pom 44 | import 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-web 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-data-jpa 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-starter-actuator 60 | 61 | 62 | org.springframework.cloud 63 | spring-cloud-starter-kubernetes-config 64 | 65 | 66 | com.h2database 67 | h2 68 | 69 | 70 | org.postgresql 71 | postgresql 72 | ${postgresql.version} 73 | 74 | 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-compiler-plugin 80 | 3.6.1 81 | 82 | 1.8 83 | 1.8 84 | 85 | 86 | 87 | org.springframework.boot 88 | spring-boot-maven-plugin 89 | ${spring-boot.version} 90 | 91 | 92 | 93 | repackage 94 | 95 | 96 | 97 | 98 | -Djava.net.preferIPv4Stack=true -Dserver.port=9000 -Dspring.cloud.kubernetes.enabled=false 99 | 100 | 101 | 102 | io.fabric8 103 | fabric8-maven-plugin 104 | ${fabric8.maven.plugin.version} 105 | 106 | 107 | install 108 | 109 | resource 110 | build 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | app 120 | catalog 121 | 122 | 123 | 124 | 125 | 126 | 127 | spring-boot-health-check 128 | 129 | 130 | 131 | 132 | spring-boot 133 | 134 | 135 | 136 | registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift:1.4 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /catalog-spring-boot/src/main/java/com/redhat/cloudnative/catalog/CatalogApplication.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.catalog; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class CatalogApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(CatalogApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /catalog-spring-boot/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=catalog 2 | 3 | spring.datasource.url=jdbc:h2:mem:fruits;DB_CLOSE_ON_EXIT=FALSE 4 | spring.datasource.username=sa 5 | spring.datasource.password= 6 | spring.datasource.driver-class-name=org.h2.Driver 7 | -------------------------------------------------------------------------------- /catalog-spring-boot/src/main/resources/import.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('329299', 'Red Fedora', 'Official Red Hat Fedora', 34.99); 2 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('329199', 'Forge Laptop Sticker', 'JBoss Community Forge Project Sticker', 8.50); 3 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('165613', 'Solid Performance Polo', 'Moisture-wicking, antimicrobial 100% polyester design wicks for life of garment. No-curl, rib-knit collar...', 17.80) 4 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('165614', 'Ogio Caliber Polo', 'Moisture-wicking 100% polyester. Rib-knit collar and cuffs; Ogio jacquard tape insitem_ide neck; bar-tacked three-button placket with...', 28.75) 5 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('165954', '16 oz. Vortex Tumbler', 'Double-wall insulated, BPA-free, acrylic cup. Push-on litem_id with thumb-slitem_ide closure; for hot and cold beverages. Holds 16 oz. Hand wash only. Imprint. Clear.', 6.00) 6 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('444434', 'Pebble Smart Watch', 'Smart glasses and smart watches are perhaps two of the most exciting developments in recent years. ', 24.00) 7 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('444435', 'Oculus Rift', 'The world of gaming has also undergone some very unique and compelling tech advances in recent years. Virtual reality...', 106.00) 8 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('444436', 'Lytro Camera', 'Consumers who want to up their photography game are looking at newfangled cameras like the Lytro Field camera, designed to ...', 44.30) 9 | -------------------------------------------------------------------------------- /catalog-spring-boot/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Catalog Service 6 | 8 | 10 | 12 | 13 | 14 |
15 |
16 |

Spring Boot Catalog Service

17 |

This is a Spring Boot Microservice for the CoolStore Demo. (Test it) 18 |

19 |
20 |
21 |
22 | 25 |
26 | 27 | -------------------------------------------------------------------------------- /catalog-spring-boot/src/main/resources/static/spring-boot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/catalog-spring-boot/src/main/resources/static/spring-boot.png -------------------------------------------------------------------------------- /gateway-vertx/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.iml 3 | *.log 4 | *.class 5 | tmp/ 6 | .idea/ 7 | .openshiftio 8 | .settings/ 9 | .classpath 10 | .project 11 | .DS_Store 12 | bower_components/ 13 | node_modules/ -------------------------------------------------------------------------------- /gateway-vertx/openshift/istio-gateway.yml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: Gateway 3 | metadata: 4 | name: istio-gateway 5 | spec: 6 | selector: 7 | istio: ingressgateway # use Istio default gateway implementation 8 | servers: 9 | - port: 10 | number: 80 11 | name: http 12 | protocol: HTTP 13 | hosts: 14 | - "*" -------------------------------------------------------------------------------- /gateway-vertx/openshift/virtualservice.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: networking.istio.io/v1alpha3 3 | kind: VirtualService 4 | metadata: 5 | name: gateway-COOLSTORE_PROJECT 6 | spec: 7 | hosts: 8 | - "*" 9 | gateways: 10 | - istio-gateway 11 | http: 12 | - match: 13 | - uri: 14 | prefix: /COOLSTORE_PROJECT/api 15 | rewrite: 16 | uri: "/api" 17 | route: 18 | - destination: 19 | port: 20 | number: 8080 21 | host: gateway -------------------------------------------------------------------------------- /gateway-vertx/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.redhat.cloudnative 7 | gateway 8 | 1.0-SNAPSHOT 9 | jar 10 | 11 | CoolStore Gateway Service 12 | CoolStore Gateway Service with Eclipse Vert.x 13 | 14 | 15 | 3.6.3.redhat-00009 16 | 1.0.15 17 | com.redhat.cloudnative.gateway.GatewayVerticle 18 | 3.5.40 19 | 1.7.21 20 | 21 | 22 | 23 | 24 | redhat-ga 25 | https://maven.repository.redhat.com/ga/ 26 | 27 | 28 | 29 | 30 | redhat-ga-plugins 31 | https://maven.repository.redhat.com/ga/ 32 | 33 | 34 | 35 | 36 | 37 | 38 | io.vertx 39 | vertx-dependencies 40 | ${vertx.version} 41 | pom 42 | import 43 | 44 | 45 | 46 | 47 | 48 | 49 | io.vertx 50 | vertx-core 51 | 52 | 53 | io.vertx 54 | vertx-web 55 | 56 | 57 | io.vertx 58 | vertx-circuit-breaker 59 | 60 | 61 | io.vertx 62 | vertx-web-client 63 | 64 | 65 | io.vertx 66 | vertx-rx-java2 67 | 68 | 69 | io.vertx 70 | vertx-service-discovery 71 | 72 | 73 | io.vertx 74 | vertx-service-discovery-bridge-kubernetes 75 | 76 | 77 | org.slf4j 78 | slf4j-api 79 | ${slf4j.version} 80 | 81 | 82 | org.slf4j 83 | slf4j-jdk14 84 | ${slf4j.version} 85 | 86 | 87 | 88 | 89 | 90 | 91 | org.apache.maven.plugins 92 | maven-compiler-plugin 93 | 3.6.1 94 | 95 | 1.8 96 | 1.8 97 | 98 | 99 | 100 | io.reactiverse 101 | vertx-maven-plugin 102 | ${vertx-maven-plugin.version} 103 | 104 | 105 | vmp 106 | 107 | initialize 108 | package 109 | 110 | 111 | 112 | 113 | true 114 | -Djava.net.preferIPv4Stack=true 115 | 116 | 117 | 118 | io.fabric8 119 | fabric8-maven-plugin 120 | ${fabric8.maven.plugin.version} 121 | 122 | 123 | install 124 | 125 | resource 126 | build 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | app 136 | gateway 137 | 138 | 139 | 140 | 141 | 142 | 143 | vertx-health-check 144 | 145 | 146 | 147 | 148 | vertx 149 | 150 | 151 | 152 | registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift:1.4 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /gateway-vertx/src/main/java/com/redhat/cloudnative/gateway/GatewayVerticle.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.gateway; 2 | 3 | 4 | import io.vertx.core.Future; 5 | import io.vertx.reactivex.core.AbstractVerticle; 6 | import io.vertx.reactivex.ext.web.Router; 7 | import io.vertx.reactivex.ext.web.handler.StaticHandler; 8 | 9 | public class GatewayVerticle extends AbstractVerticle { 10 | @Override 11 | public void start(Future future) { 12 | Router router = Router.router(vertx); 13 | 14 | router.get("/*").handler(StaticHandler.create("assets")); 15 | 16 | vertx.createHttpServer().requestHandler(router) 17 | .listen(Integer.getInteger("http.port", 8080)); 18 | } 19 | } -------------------------------------------------------------------------------- /gateway-vertx/src/main/resources/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Gateway Service 6 | 8 | 10 | 12 | 13 | 14 |
15 |
16 |

Vert.x Gateway Service

17 |

This is a Vert.x Microservice for the CoolStore Demo. (Test it) 18 |

19 |
20 |
21 |
22 |
23 |

© Red Hat 2019

24 |
25 |
26 | 27 | -------------------------------------------------------------------------------- /gateway-vertx/src/main/resources/assets/vert.x_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/gateway-vertx/src/main/resources/assets/vert.x_logo.png -------------------------------------------------------------------------------- /gateway-vertx/src/main/resources/vertx-default-jul-logging.properties: -------------------------------------------------------------------------------- 1 | 2 | handlers=java.util.logging.ConsoleHandler 3 | java.util.logging.SimpleFormatter.format=[%p] %t: %m%n\n 4 | java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter 5 | java.util.logging.ConsoleHandler.level=FINEST 6 | 7 | .level=INFO 8 | io.vertx.ext.web.level=INFO 9 | io.vertx.level=INFO 10 | com.hazelcast.level=INFO 11 | io.netty.util.internal.PlatformDependent.level=SEVERE 12 | io.vertx.servicediscovery.level=SEVERE 13 | -------------------------------------------------------------------------------- /gateway-vertx/src/test/resources/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "http.port": 8080, 3 | "catalog.url": "http://localhost:9000", 4 | "inventory.url": "http://localhost:9001" 5 | } -------------------------------------------------------------------------------- /inventory-thorntail/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.iml 3 | *.log 4 | *.class 5 | tmp/ 6 | .idea/ 7 | .openshiftio 8 | .settings/ 9 | .classpath 10 | .project 11 | .DS_Store 12 | .vscode 13 | bower_components/ 14 | node_modules/ 15 | -------------------------------------------------------------------------------- /inventory-thorntail/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.redhat.cloudnative 7 | inventory 8 | 1.0-SNAPSHOT 9 | war 10 | CoolStore Inventory Service 11 | CoolStore Inventory Service with Thorntail 12 | 13 | 2.2.0.Final-redhat-00021 14 | 3.0.12 15 | 3.5.40 16 | 1.4.187 17 | 42.2.4 18 | false 19 | 20 | 21 | 22 | redhat-ga 23 | https://maven.repository.redhat.com/ga/ 24 | 25 | 26 | 27 | 28 | redhat-ga-plugins 29 | https://maven.repository.redhat.com/ga/ 30 | 31 | 32 | 33 | 34 | 35 | io.thorntail 36 | bom 37 | ${version.thorntail} 38 | pom 39 | import 40 | 41 | 42 | io.fabric8 43 | fabric8-project-bom-with-platform-deps 44 | ${fabric8.version} 45 | pom 46 | import 47 | 48 | 49 | 50 | 51 | 52 | io.thorntail 53 | jaxrs-jsonp 54 | 55 | 56 | io.thorntail 57 | cdi 58 | 59 | 60 | io.thorntail 61 | microprofile-restclient 62 | 63 | 64 | io.thorntail 65 | microprofile-health 66 | 67 | 68 | com.h2database 69 | h2 70 | ${h2.version} 71 | 72 | 73 | org.postgresql 74 | postgresql 75 | ${postgresql.version} 76 | 77 | 78 | 79 | 80 | 81 | org.apache.maven.plugins 82 | maven-compiler-plugin 83 | 3.6.1 84 | 85 | 1.8 86 | 1.8 87 | 88 | 89 | 90 | maven-war-plugin 91 | 3.1.0 92 | 93 | false 94 | 95 | 96 | 97 | io.thorntail 98 | thorntail-maven-plugin 99 | ${version.thorntail} 100 | 101 | 102 | 103 | package 104 | 105 | 106 | 107 | 108 | 109 | true 110 | 111 | 112 | -Dthorntail.http.port=9001 113 | 114 | 115 | 116 | 117 | io.fabric8 118 | fabric8-maven-plugin 119 | ${fabric8.maven.plugin.version} 120 | 121 | 122 | install 123 | 124 | resource 125 | build 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | app 135 | inventory 136 | 137 | 138 | 139 | 140 | 300 141 | 142 | 143 | 144 | 145 | thorntail-v2 146 | 147 | 148 | 149 | registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift:1.4 150 | 151 | 152 | 153 | 154 | 155 | thorntail-v2-health-check 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /inventory-thorntail/src/main/java/com/redhat/cloudnative/inventory/InventoryApplication.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.inventory; 2 | 3 | import javax.ws.rs.ApplicationPath; 4 | import javax.ws.rs.core.Application; 5 | 6 | @ApplicationPath("/api") 7 | public class InventoryApplication extends Application { 8 | } 9 | -------------------------------------------------------------------------------- /inventory-thorntail/src/main/resources/META-INF/load.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO INVENTORY(itemId, quantity) VALUES (329299, 35) 2 | INSERT INTO INVENTORY(itemId, quantity) VALUES (329199, 12) 3 | INSERT INTO INVENTORY(itemId, quantity) VALUES (165613, 45) 4 | INSERT INTO INVENTORY(itemId, quantity) VALUES (165614, 87) 5 | INSERT INTO INVENTORY(itemId, quantity) VALUES (165954, 43) 6 | INSERT INTO INVENTORY(itemId, quantity) VALUES (444434, 32) 7 | INSERT INTO INVENTORY(itemId, quantity) VALUES (444435, 53) 8 | -------------------------------------------------------------------------------- /inventory-thorntail/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 23 | 24 | java:/jboss/datasources/InventoryDS 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /inventory-thorntail/src/main/resources/project-defaults.yml: -------------------------------------------------------------------------------- 1 | swarm: 2 | datasources: 3 | data-sources: 4 | InventoryDS: 5 | driver-name: h2 6 | connection-url: jdbc:h2:mem:fruits;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE 7 | user-name: sa 8 | password: sa -------------------------------------------------------------------------------- /inventory-thorntail/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 23 | -------------------------------------------------------------------------------- /inventory-thorntail/src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Inventory Service 6 | 8 | 10 | 12 | 13 | 14 |
15 |
16 |

Thorntail Inventory Service

17 |

This is a Thorntail Microservice for the CoolStore Demo. (Test it) 18 |

19 |
20 |
21 |
22 |
23 |

© Red Hat 2019

24 |
25 |
26 | 27 | -------------------------------------------------------------------------------- /inventory-thorntail/src/main/webapp/thorntail_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/inventory-thorntail/src/main/webapp/thorntail_icon.png -------------------------------------------------------------------------------- /inventory-vertx/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.iml 3 | *.log 4 | *.class 5 | tmp/ 6 | .idea/ 7 | .openshiftio 8 | .settings/ 9 | .classpath 10 | .project 11 | .DS_Store 12 | .vscode 13 | bower_components/ 14 | node_modules/ 15 | -------------------------------------------------------------------------------- /inventory-vertx/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.redhat.cloudnative 7 | inventory 8 | 1.0-SNAPSHOT 9 | jar 10 | CoolStore Inventory Service 11 | CoolStore Inventory Service with Vertx 12 | 13 | 3.6.3.redhat-00009 14 | 1.0.15 15 | com.redhat.cloudnative.inventory.InventoryVerticle 16 | 1.7.21 17 | 2.2.0.Final-redhat-00021 18 | 3.0.12 19 | 3.5.40 20 | 1.4.187 21 | 42.2.4 22 | false 23 | 24 | 25 | 26 | redhat-ga 27 | https://maven.repository.redhat.com/ga/ 28 | 29 | 30 | 31 | 32 | redhat-ga-plugins 33 | https://maven.repository.redhat.com/ga/ 34 | 35 | 36 | 37 | 38 | 39 | io.vertx 40 | vertx-dependencies 41 | ${vertx.version} 42 | pom 43 | import 44 | 45 | 46 | io.thorntail 47 | bom 48 | ${version.thorntail} 49 | pom 50 | import 51 | 52 | 53 | io.fabric8 54 | fabric8-project-bom-with-platform-deps 55 | ${fabric8.version} 56 | pom 57 | import 58 | 59 | 60 | 61 | 62 | 63 | io.vertx 64 | vertx-core 65 | 66 | 67 | io.vertx 68 | vertx-config 69 | 70 | 71 | io.vertx 72 | vertx-config-kubernetes-configmap 73 | 74 | 75 | io.vertx 76 | vertx-config-yaml 77 | 78 | 79 | io.vertx 80 | vertx-web 81 | 82 | 83 | io.vertx 84 | vertx-jdbc-client 85 | 86 | 87 | io.agroal 88 | agroal-api 89 | 1.3.0.redhat-00001 90 | 91 | 92 | io.agroal 93 | agroal-pool 94 | 1.3.0.redhat-00001 95 | 96 | 97 | io.vertx 98 | vertx-rx-java2 99 | 100 | 101 | org.slf4j 102 | slf4j-api 103 | ${slf4j.version} 104 | 105 | 106 | org.slf4j 107 | slf4j-jdk14 108 | ${slf4j.version} 109 | 110 | 111 | com.h2database 112 | h2 113 | ${h2.version} 114 | 115 | 116 | org.postgresql 117 | postgresql 118 | ${postgresql.version} 119 | 120 | 121 | 122 | 123 | 124 | org.apache.maven.plugins 125 | maven-compiler-plugin 126 | 3.6.1 127 | 128 | 1.8 129 | 1.8 130 | 131 | 132 | 133 | io.reactiverse 134 | vertx-maven-plugin 135 | ${vertx-maven-plugin.version} 136 | 137 | 138 | vmp 139 | 140 | initialize 141 | package 142 | 143 | 144 | 145 | 146 | true 147 | -Djava.net.preferIPv4Stack=true -Dhttp.port=9001 148 | 149 | 150 | 151 | io.fabric8 152 | fabric8-maven-plugin 153 | ${fabric8.maven.plugin.version} 154 | 155 | 156 | install 157 | 158 | resource 159 | build 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | app 169 | inventory 170 | 171 | 172 | 173 | 174 | 175 | 176 | vertx-health-check 177 | 178 | 179 | 180 | 181 | vertx 182 | 183 | 184 | 185 | registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift:1.4 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /inventory-vertx/src/main/java/com/redhat/cloudnative/inventory/InventoryVerticle.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.inventory; 2 | 3 | import io.reactivex.Completable; 4 | import io.reactivex.Single; 5 | import io.vertx.reactivex.config.ConfigRetriever; 6 | import io.vertx.config.ConfigRetrieverOptions; 7 | import io.vertx.config.ConfigStoreOptions; 8 | import io.vertx.core.json.JsonObject; 9 | import io.vertx.reactivex.core.http.HttpServer; 10 | import io.vertx.reactivex.ext.jdbc.JDBCClient; 11 | import io.vertx.ext.sql.ResultSet; 12 | import io.vertx.reactivex.ext.web.Router; 13 | import io.vertx.reactivex.core.AbstractVerticle; 14 | import io.vertx.reactivex.ext.web.handler.StaticHandler; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | public class InventoryVerticle extends AbstractVerticle { 19 | 20 | private static final Logger LOG = LoggerFactory.getLogger(InventoryVerticle.class); 21 | 22 | private JDBCClient inventoryClient; 23 | 24 | @Override 25 | public Completable rxStart() { 26 | 27 | Router router = Router.router(vertx); 28 | router.get("/*").handler(StaticHandler.create("assets")); 29 | router.get("/health").handler(ctx -> ctx.response().end(new JsonObject().put("status", "UP").toString())); 30 | 31 | Single serverSingle = vertx.createHttpServer() 32 | .requestHandler(router) 33 | .rxListen(Integer.getInteger("http.port", 8080)); 34 | 35 | ConfigRetrieverOptions configOptions = new ConfigRetrieverOptions() 36 | .addStore(new ConfigStoreOptions() 37 | .setType("file") 38 | .setFormat("yaml") 39 | .setConfig(new JsonObject() 40 | .put("path", "config/app-config.yml"))); 41 | ConfigRetriever retriever = ConfigRetriever.create(vertx, configOptions); 42 | Single s = retriever.rxGetConfig(); 43 | 44 | return s 45 | .flatMap(this::populateDatabase) 46 | .flatMap(rs -> serverSingle) 47 | .ignoreElement(); 48 | } 49 | 50 | private Single populateDatabase(JsonObject config) { 51 | LOG.info("Will use database " + config.getValue("jdbcUrl")); 52 | inventoryClient = JDBCClient.createNonShared(vertx, config); 53 | String sql = "" + 54 | "drop table if exists INVENTORY;" + 55 | "create table \"INVENTORY\" (\"ITEMID\" varchar(32) PRIMARY KEY, \"QUANTITY\" int);" + 56 | "insert into \"INVENTORY\" (\"ITEMID\", \"QUANTITY\") values (329299, 35);" + 57 | "insert into \"INVENTORY\" (\"ITEMID\", \"QUANTITY\") values (329199, 12);" + 58 | "insert into \"INVENTORY\" (\"ITEMID\", \"QUANTITY\") values (165613, 45);" + 59 | "insert into \"INVENTORY\" (\"ITEMID\", \"QUANTITY\") values (165614, 87);" + 60 | "insert into \"INVENTORY\" (\"ITEMID\", \"QUANTITY\") values (165954, 43);" + 61 | "insert into \"INVENTORY\" (\"ITEMID\", \"QUANTITY\") values (444434, 32);" + 62 | "insert into \"INVENTORY\" (\"ITEMID\", \"QUANTITY\") values (444435, 53);"; 63 | return inventoryClient.rxQuery(sql); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /inventory-vertx/src/main/resources/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Inventory Service 6 | 8 | 10 | 12 | 13 | 14 |
15 |
16 |

Vert.x Inventory Service

17 |

This is a Vert.x Microservice for the CoolStore Demo. (Test it) 18 |

19 |
20 |
21 |
22 |
23 |

© Red Hat 2019

24 |
25 |
26 | 27 | -------------------------------------------------------------------------------- /inventory-vertx/src/main/resources/assets/vert.x_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/inventory-vertx/src/main/resources/assets/vert.x_logo.png -------------------------------------------------------------------------------- /inventory-vertx/src/main/resources/config/app-config.yml: -------------------------------------------------------------------------------- 1 | driverClassName: org.h2.jdbcx.JdbcDataSource 2 | jdbcUrl: jdbc:h2:mem:fruits;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE 3 | principal: sa 4 | credential: sa 5 | -------------------------------------------------------------------------------- /inventory-vertx/src/main/resources/vertx-default-jul-logging.properties: -------------------------------------------------------------------------------- 1 | 2 | handlers=java.util.logging.ConsoleHandler 3 | java.util.logging.SimpleFormatter.format=[%p] %t: %m%n\n 4 | java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter 5 | java.util.logging.ConsoleHandler.level=FINEST 6 | 7 | .level=INFO 8 | io.vertx.ext.web.level=INFO 9 | io.vertx.level=INFO 10 | com.hazelcast.level=INFO 11 | io.netty.util.internal.PlatformDependent.level=SEVERE 12 | io.vertx.servicediscovery.level=SEVERE 13 | -------------------------------------------------------------------------------- /scripts/runGatewayService.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | project_name=$1 4 | if [ -z "${project_name}" ] 5 | then 6 | echo "Usage: ./runGatewayService.sh " 7 | exit 8 | fi 9 | 10 | url=http://istio-ingressgateway.istio-system.svc/${project_name}/api/products 11 | 12 | while true; do 13 | if curl -s ${url} | grep -q OFFICIAL 14 | then 15 | echo "Gateway => Catalog GoLang (v2)"; 16 | else 17 | echo "Gateway => Catalog Spring Boot (v1)"; 18 | fi 19 | sleep 1 20 | done -------------------------------------------------------------------------------- /solutions/all/catalog-spring-boot/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.iml 3 | *.log 4 | *.class 5 | tmp/ 6 | .idea/ 7 | .openshiftio 8 | .settings/ 9 | .classpath 10 | .project 11 | .DS_Store 12 | bower_components/ 13 | node_modules/ -------------------------------------------------------------------------------- /solutions/all/catalog-spring-boot/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.redhat.cloudnative 7 | catalog 8 | 1.0-SNAPSHOT 9 | CoolStore Catalog Service 10 | CoolStore Catalog Service with Spring Boot 11 | 12 | 1.5.10.RELEASE 13 | 1.5.10.SP2-redhat-2 14 | 0.2.0.RELEASE 15 | 3.1.10 16 | 3.5.40 17 | 42.2.4 18 | 19 | 20 | 21 | redhat-ga 22 | https://maven.repository.redhat.com/ga/ 23 | 24 | 25 | 26 | 27 | redhat-ga-plugins 28 | https://maven.repository.redhat.com/ga/ 29 | 30 | 31 | 32 | 33 | 34 | me.snowdrop 35 | spring-boot-bom 36 | ${spring-boot.bom.version} 37 | pom 38 | import 39 | 40 | 41 | org.springframework.cloud 42 | spring-cloud-kubernetes-dependencies 43 | ${spring.k8s.bom.version} 44 | pom 45 | import 46 | 47 | 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-starter-web 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-starter-data-jpa 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-starter-actuator 61 | 62 | 63 | org.springframework.cloud 64 | spring-cloud-starter-kubernetes-config 65 | 66 | 67 | io.fabric8 68 | kubernetes-client 69 | ${k8s.client.version} 70 | 71 | 72 | com.h2database 73 | h2 74 | 75 | 76 | org.postgresql 77 | postgresql 78 | ${postgresql.version} 79 | 80 | 81 | 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-compiler-plugin 86 | 3.6.1 87 | 88 | 1.8 89 | 1.8 90 | 91 | 92 | 93 | org.springframework.boot 94 | spring-boot-maven-plugin 95 | ${spring-boot.version} 96 | 97 | 98 | 99 | repackage 100 | 101 | 102 | 103 | 104 | -Djava.net.preferIPv4Stack=true -Dserver.port=9000 -Dspring.cloud.kubernetes.enabled=false 105 | 106 | 107 | 108 | io.fabric8 109 | fabric8-maven-plugin 110 | ${fabric8.maven.plugin.version} 111 | 112 | 113 | install 114 | 115 | resource 116 | build 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | app 126 | catalog 127 | 128 | 129 | 130 | 131 | 132 | 133 | spring-boot 134 | 135 | 136 | 137 | registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift:1.4 138 | 139 | 140 | 141 | 142 | 143 | 144 | /health 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /solutions/all/catalog-spring-boot/src/main/java/com/redhat/cloudnative/catalog/CatalogApplication.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.catalog; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class CatalogApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(CatalogApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /solutions/all/catalog-spring-boot/src/main/java/com/redhat/cloudnative/catalog/CatalogController.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.catalog; 2 | 3 | import java.util.List; 4 | import java.util.Spliterator; 5 | import java.util.stream.Collectors; 6 | import java.util.stream.StreamSupport; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.http.MediaType; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.ResponseBody; 14 | 15 | @Controller 16 | @RequestMapping(value = "/api/catalog") 17 | public class CatalogController { 18 | 19 | @Autowired 20 | private ProductRepository repository; 21 | 22 | @ResponseBody 23 | @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) 24 | public List getAll() { 25 | Spliterator products = repository.findAll().spliterator(); 26 | return StreamSupport.stream(products, false).collect(Collectors.toList()); 27 | } 28 | } -------------------------------------------------------------------------------- /solutions/all/catalog-spring-boot/src/main/java/com/redhat/cloudnative/catalog/Product.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.catalog; 2 | 3 | import java.io.Serializable; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.Id; 7 | import javax.persistence.Table; 8 | import javax.persistence.UniqueConstraint; 9 | 10 | @Entity 11 | @Table(name = "PRODUCT", uniqueConstraints = @UniqueConstraint(columnNames = "itemId")) 12 | public class Product implements Serializable { 13 | 14 | @Id 15 | private String itemId; 16 | 17 | private String name; 18 | 19 | private String description; 20 | 21 | private double price; 22 | 23 | public Product() { 24 | } 25 | 26 | public String getItemId() { 27 | return itemId; 28 | } 29 | 30 | public void setItemId(String itemId) { 31 | this.itemId = itemId; 32 | } 33 | 34 | public String getName() { 35 | return name; 36 | } 37 | 38 | public void setName(String name) { 39 | this.name = name; 40 | } 41 | 42 | public String getDescription() { 43 | return description; 44 | } 45 | 46 | public void setDescription(String description) { 47 | this.description = description; 48 | } 49 | 50 | public double getPrice() { 51 | return price; 52 | } 53 | 54 | public void setPrice(double price) { 55 | this.price = price; 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return "Product [itemId=" + itemId + ", name=" + name + ", price=" + price + "]"; 61 | } 62 | } -------------------------------------------------------------------------------- /solutions/all/catalog-spring-boot/src/main/java/com/redhat/cloudnative/catalog/ProductRepository.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.catalog; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | public interface ProductRepository extends CrudRepository { 6 | } 7 | -------------------------------------------------------------------------------- /solutions/all/catalog-spring-boot/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=catalog 2 | 3 | spring.datasource.url=jdbc:h2:mem:fruits;DB_CLOSE_ON_EXIT=FALSE 4 | spring.datasource.username=sa 5 | spring.datasource.password= 6 | spring.datasource.driver-class-name=org.h2.Driver 7 | -------------------------------------------------------------------------------- /solutions/all/catalog-spring-boot/src/main/resources/import.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('329299', 'Red Fedora', 'Official Red Hat Fedora', 34.99); 2 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('329199', 'Forge Laptop Sticker', 'JBoss Community Forge Project Sticker', 8.50); 3 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('165613', 'Solid Performance Polo', 'Moisture-wicking, antimicrobial 100% polyester design wicks for life of garment. No-curl, rib-knit collar...', 17.80) 4 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('165614', 'Ogio Caliber Polo', 'Moisture-wicking 100% polyester. Rib-knit collar and cuffs; Ogio jacquard tape insitem_ide neck; bar-tacked three-button placket with...', 28.75) 5 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('165954', '16 oz. Vortex Tumbler', 'Double-wall insulated, BPA-free, acrylic cup. Push-on litem_id with thumb-slitem_ide closure; for hot and cold beverages. Holds 16 oz. Hand wash only. Imprint. Clear.', 6.00) 6 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('444434', 'Pebble Smart Watch', 'Smart glasses and smart watches are perhaps two of the most exciting developments in recent years. ', 24.00) 7 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('444435', 'Oculus Rift', 'The world of gaming has also undergone some very unique and compelling tech advances in recent years. Virtual reality...', 106.00) 8 | INSERT INTO PRODUCT (item_id, name, description, price) VALUES ('444436', 'Lytro Camera', 'Consumers who want to up their photography game are looking at newfangled cameras like the Lytro Field camera, designed to ...', 44.30) 9 | -------------------------------------------------------------------------------- /solutions/all/coolstore-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Template 3 | metadata: 4 | name: coolstore 5 | labels: 6 | application: "coolstore" 7 | parameters: 8 | - description: Git source URI for application 9 | displayName: Git Repository 10 | name: GIT_URI 11 | required: true 12 | value: https://github.com/openshift-labs/cloud-native-labs.git 13 | - description: Git branch/tag reference 14 | displayName: Git Branch/Tag 15 | name: GIT_REF 16 | required: true 17 | value: ocp-3.11 18 | - description: Maven mirror url e.g. Artifactory or Nexus http://nexus.ci:8081/content/groups/public/ 19 | displayName: Maven Mirror URL 20 | name: MAVEN_MIRROR_URL 21 | objects: 22 | - apiVersion: v1 23 | kind: ImageStream 24 | metadata: 25 | labels: 26 | app: catalog 27 | name: catalog 28 | - apiVersion: v1 29 | kind: ImageStream 30 | metadata: 31 | labels: 32 | app: gateway 33 | name: gateway 34 | - apiVersion: v1 35 | kind: ImageStream 36 | metadata: 37 | labels: 38 | app: inventory 39 | name: inventory 40 | - apiVersion: v1 41 | kind: ImageStream 42 | metadata: 43 | labels: 44 | app: web 45 | name: web 46 | - apiVersion: v1 47 | kind: BuildConfig 48 | metadata: 49 | labels: 50 | app: catalog 51 | name: catalog-s2i 52 | spec: 53 | output: 54 | to: 55 | kind: ImageStreamTag 56 | name: catalog:latest 57 | runPolicy: Serial 58 | source: 59 | contextDir: solutions/all/catalog-spring-boot 60 | git: 61 | ref: ${GIT_REF} 62 | uri: ${GIT_URI} 63 | type: Git 64 | strategy: 65 | sourceStrategy: 66 | env: 67 | - name: MAVEN_MIRROR_URL 68 | value: ${MAVEN_MIRROR_URL} 69 | from: 70 | kind: ImageStreamTag 71 | name: java:8 72 | namespace: openshift 73 | type: Source 74 | triggers: 75 | - generic: 76 | secret: 26f0d384fdab122b 77 | type: Generic 78 | - github: 79 | secret: b3c6f1f04b70ee2a 80 | type: GitHub 81 | - imageChange: 82 | type: ImageChange 83 | - type: ConfigChange 84 | - apiVersion: v1 85 | kind: BuildConfig 86 | metadata: 87 | name: gateway-s2i 88 | labels: 89 | app: gateway 90 | spec: 91 | output: 92 | to: 93 | kind: ImageStreamTag 94 | name: gateway:latest 95 | source: 96 | contextDir: solutions/all/gateway-vertx 97 | git: 98 | ref: ${GIT_REF} 99 | uri: ${GIT_URI} 100 | type: Git 101 | strategy: 102 | sourceStrategy: 103 | env: 104 | - name: MAVEN_MIRROR_URL 105 | value: ${MAVEN_MIRROR_URL} 106 | from: 107 | kind: ImageStreamTag 108 | namespace: openshift 109 | name: java:8 110 | type: Source 111 | triggers: 112 | - github: 113 | secret: KyiqYOEkn7w2ETK_LAUL 114 | type: GitHub 115 | - generic: 116 | secret: 9VB9eGj2Vvt1GJRiGBKq 117 | type: Generic 118 | - type: ConfigChange 119 | - imageChange: {} 120 | type: ImageChange 121 | - apiVersion: v1 122 | kind: BuildConfig 123 | metadata: 124 | labels: 125 | app: inventory 126 | name: inventory-s2i 127 | spec: 128 | output: 129 | to: 130 | kind: ImageStreamTag 131 | name: inventory:latest 132 | source: 133 | contextDir: solutions/all/inventory-wildfly-swarm 134 | git: 135 | ref: ${GIT_REF} 136 | uri: ${GIT_URI} 137 | type: Git 138 | strategy: 139 | sourceStrategy: 140 | env: 141 | - name: MAVEN_MIRROR_URL 142 | value: ${MAVEN_MIRROR_URL} 143 | from: 144 | kind: ImageStreamTag 145 | namespace: openshift 146 | name: java:8 147 | type: Source 148 | triggers: 149 | - github: 150 | secret: KyiqYOEkn7w2ETK_LAUL 151 | type: GitHub 152 | - generic: 153 | secret: 9VB9eGj2Vvt1GJRiGBKq 154 | type: Generic 155 | - type: ConfigChange 156 | - imageChange: {} 157 | type: ImageChange 158 | - apiVersion: v1 159 | kind: BuildConfig 160 | metadata: 161 | labels: 162 | app: web 163 | name: web 164 | spec: 165 | output: 166 | to: 167 | kind: ImageStreamTag 168 | name: web:latest 169 | source: 170 | contextDir: web-nodejs 171 | git: 172 | ref: ${GIT_REF} 173 | uri: ${GIT_URI} 174 | type: Git 175 | strategy: 176 | sourceStrategy: 177 | from: 178 | kind: ImageStreamTag 179 | name: nodejs:4 180 | namespace: openshift 181 | type: Source 182 | triggers: 183 | - github: 184 | secret: KyiqYOEkn7w2ETK_LAUL 185 | type: GitHub 186 | - generic: 187 | secret: 9VB9eGj2Vvt1GJRiGBKq 188 | type: Generic 189 | - type: ConfigChange 190 | - imageChange: {} 191 | type: ImageChange 192 | - apiVersion: v1 193 | kind: DeploymentConfig 194 | metadata: 195 | labels: 196 | app: catalog 197 | name: catalog 198 | spec: 199 | replicas: 1 200 | selector: 201 | deploymentconfig: catalog 202 | app: catalog 203 | strategy: 204 | activeDeadlineSeconds: 21600 205 | resources: {} 206 | rollingParams: 207 | intervalSeconds: 1 208 | maxSurge: 25% 209 | maxUnavailable: 25% 210 | timeoutSeconds: 600 211 | updatePeriodSeconds: 1 212 | type: Rolling 213 | template: 214 | metadata: 215 | labels: 216 | app: catalog 217 | deploymentconfig: catalog 218 | spec: 219 | containers: 220 | - image: catalog 221 | imagePullPolicy: Always 222 | name: catalog 223 | ports: 224 | - containerPort: 8080 225 | protocol: TCP 226 | name: http 227 | - containerPort: 8787 228 | protocol: TCP 229 | name: jolokia 230 | terminationMessagePath: /dev/termination-log 231 | livenessProbe: 232 | failureThreshold: 3 233 | httpGet: 234 | path: /health 235 | port: 8080 236 | scheme: HTTP 237 | initialDelaySeconds: 180 238 | periodSeconds: 10 239 | successThreshold: 1 240 | timeoutSeconds: 1 241 | readinessProbe: 242 | failureThreshold: 3 243 | httpGet: 244 | path: /health 245 | port: 8080 246 | scheme: HTTP 247 | initialDelaySeconds: 10 248 | periodSeconds: 10 249 | successThreshold: 1 250 | timeoutSeconds: 1 251 | dnsPolicy: ClusterFirst 252 | restartPolicy: Always 253 | terminationGracePeriodSeconds: 30 254 | triggers: 255 | - imageChangeParams: 256 | automatic: true 257 | containerNames: 258 | - catalog 259 | from: 260 | kind: ImageStreamTag 261 | name: catalog:latest 262 | type: ImageChange 263 | - type: ConfigChange 264 | - apiVersion: v1 265 | kind: DeploymentConfig 266 | metadata: 267 | labels: 268 | app: gateway 269 | name: gateway 270 | spec: 271 | replicas: 1 272 | selector: 273 | deploymentconfig: gateway 274 | app: gateway 275 | strategy: 276 | activeDeadlineSeconds: 21600 277 | rollingParams: 278 | intervalSeconds: 1 279 | maxSurge: 25% 280 | maxUnavailable: 25% 281 | timeoutSeconds: 3600 282 | updatePeriodSeconds: 1 283 | type: Rolling 284 | template: 285 | metadata: 286 | labels: 287 | deploymentconfig: gateway 288 | app: gateway 289 | spec: 290 | containers: 291 | - env: 292 | - name: KUBERNETES_NAMESPACE 293 | valueFrom: 294 | fieldRef: 295 | apiVersion: v1 296 | fieldPath: metadata.namespace 297 | - name: JAVA_OPTIONS 298 | value: -Dvertx.cacheDirBase=/tmp 299 | image: gateway 300 | imagePullPolicy: IfNotPresent 301 | livenessProbe: 302 | failureThreshold: 3 303 | httpGet: 304 | path: /health 305 | port: 8080 306 | scheme: HTTP 307 | initialDelaySeconds: 180 308 | periodSeconds: 10 309 | successThreshold: 1 310 | timeoutSeconds: 1 311 | name: gateway 312 | ports: 313 | - containerPort: 8080 314 | name: http 315 | protocol: TCP 316 | - containerPort: 8787 317 | name: jolokia 318 | protocol: TCP 319 | readinessProbe: 320 | failureThreshold: 3 321 | httpGet: 322 | path: /health 323 | port: 8080 324 | scheme: HTTP 325 | initialDelaySeconds: 10 326 | periodSeconds: 10 327 | successThreshold: 1 328 | timeoutSeconds: 1 329 | terminationMessagePath: /dev/termination-log 330 | restartPolicy: Always 331 | triggers: 332 | - type: ConfigChange 333 | - imageChangeParams: 334 | automatic: true 335 | containerNames: 336 | - gateway 337 | from: 338 | kind: ImageStreamTag 339 | name: gateway:latest 340 | type: ImageChange 341 | - apiVersion: v1 342 | kind: DeploymentConfig 343 | metadata: 344 | labels: 345 | app: inventory 346 | name: inventory 347 | spec: 348 | replicas: 1 349 | selector: 350 | app: inventory 351 | deploymentconfig: inventory 352 | strategy: 353 | activeDeadlineSeconds: 21600 354 | rollingParams: 355 | intervalSeconds: 1 356 | maxSurge: 25% 357 | maxUnavailable: 25% 358 | timeoutSeconds: 3600 359 | updatePeriodSeconds: 1 360 | type: Rolling 361 | template: 362 | metadata: 363 | labels: 364 | app: inventory 365 | deploymentconfig: inventory 366 | spec: 367 | containers: 368 | - env: 369 | - name: KUBERNETES_NAMESPACE 370 | valueFrom: 371 | fieldRef: 372 | apiVersion: v1 373 | fieldPath: metadata.namespace 374 | - name: AB_JOLOKIA_OFF 375 | value: "true" 376 | - name: JAVA_OPTIONS 377 | value: "-Dswarm.project.stage=prod -Dswarm.project.stage.file=file:///app/config/project-stages.yml" 378 | image: inventory 379 | imagePullPolicy: IfNotPresent 380 | livenessProbe: 381 | failureThreshold: 3 382 | httpGet: 383 | path: /health 384 | port: 8080 385 | scheme: HTTP 386 | initialDelaySeconds: 180 387 | periodSeconds: 10 388 | successThreshold: 1 389 | timeoutSeconds: 1 390 | name: inventory 391 | ports: 392 | - containerPort: 8080 393 | protocol: TCP 394 | name: http 395 | - containerPort: 8787 396 | protocol: TCP 397 | name: jolokia 398 | readinessProbe: 399 | failureThreshold: 3 400 | httpGet: 401 | path: /health 402 | port: 8080 403 | scheme: HTTP 404 | initialDelaySeconds: 10 405 | periodSeconds: 10 406 | successThreshold: 1 407 | timeoutSeconds: 1 408 | terminationMessagePath: /dev/termination-log 409 | volumeMounts: 410 | - name: config 411 | mountPath: /app/config 412 | restartPolicy: Always 413 | volumes: 414 | - configMap: 415 | name: inventory 416 | items: 417 | - key: "project-stages.yml" 418 | path: "project-stages.yml" 419 | name: config 420 | triggers: 421 | - type: ConfigChange 422 | - imageChangeParams: 423 | automatic: true 424 | containerNames: 425 | - inventory 426 | from: 427 | kind: ImageStreamTag 428 | name: inventory:latest 429 | type: ImageChange 430 | - apiVersion: v1 431 | kind: DeploymentConfig 432 | metadata: 433 | labels: 434 | app: web 435 | name: web 436 | spec: 437 | replicas: 1 438 | selector: 439 | app: web 440 | deploymentconfig: web 441 | strategy: 442 | activeDeadlineSeconds: 21600 443 | rollingParams: 444 | intervalSeconds: 1 445 | maxSurge: 25% 446 | maxUnavailable: 25% 447 | timeoutSeconds: 600 448 | updatePeriodSeconds: 1 449 | type: Rolling 450 | template: 451 | metadata: 452 | labels: 453 | app: web 454 | deploymentconfig: web 455 | spec: 456 | containers: 457 | - env: 458 | - name: COOLSTORE_GW_SERVICE 459 | value: gateway 460 | image: web 461 | imagePullPolicy: Always 462 | name: web 463 | ports: 464 | - containerPort: 8080 465 | protocol: TCP 466 | terminationMessagePath: /dev/termination-log 467 | livenessProbe: 468 | failureThreshold: 3 469 | httpGet: 470 | path: / 471 | port: 8080 472 | scheme: HTTP 473 | initialDelaySeconds: 180 474 | periodSeconds: 10 475 | successThreshold: 1 476 | timeoutSeconds: 1 477 | readinessProbe: 478 | failureThreshold: 3 479 | httpGet: 480 | path: / 481 | port: 8080 482 | scheme: HTTP 483 | initialDelaySeconds: 10 484 | periodSeconds: 10 485 | successThreshold: 1 486 | timeoutSeconds: 1 487 | dnsPolicy: ClusterFirst 488 | restartPolicy: Always 489 | triggers: 490 | - type: ConfigChange 491 | - imageChangeParams: 492 | automatic: true 493 | containerNames: 494 | - web 495 | from: 496 | kind: ImageStreamTag 497 | name: web:latest 498 | type: ImageChange 499 | - apiVersion: v1 500 | kind: Service 501 | metadata: 502 | labels: 503 | app: catalog 504 | name: catalog 505 | spec: 506 | ports: 507 | - name: http 508 | port: 8080 509 | protocol: TCP 510 | targetPort: 8080 511 | selector: 512 | app: catalog 513 | deploymentconfig: catalog 514 | type: ClusterIP 515 | - apiVersion: v1 516 | kind: Service 517 | metadata: 518 | labels: 519 | app: gateway 520 | name: gateway 521 | spec: 522 | ports: 523 | - name: http 524 | port: 8080 525 | protocol: TCP 526 | targetPort: 8080 527 | selector: 528 | app: gateway 529 | deploymentconfig: gateway 530 | type: ClusterIP 531 | - apiVersion: v1 532 | kind: Service 533 | metadata: 534 | labels: 535 | app: inventory 536 | name: inventory 537 | spec: 538 | ports: 539 | - name: http 540 | port: 8080 541 | protocol: TCP 542 | targetPort: 8080 543 | selector: 544 | app: inventory 545 | deploymentconfig: inventory 546 | type: ClusterIP 547 | - apiVersion: v1 548 | kind: Service 549 | metadata: 550 | labels: 551 | app: web 552 | name: web 553 | spec: 554 | ports: 555 | - name: http 556 | port: 8080 557 | protocol: TCP 558 | targetPort: 8080 559 | selector: 560 | app: web 561 | deploymentconfig: web 562 | type: ClusterIP 563 | - apiVersion: v1 564 | kind: Route 565 | metadata: 566 | labels: 567 | app: catalog 568 | name: catalog 569 | spec: 570 | port: 571 | targetPort: 8080 572 | to: 573 | kind: Service 574 | name: catalog 575 | weight: 100 576 | - apiVersion: v1 577 | kind: Route 578 | metadata: 579 | labels: 580 | app: inventory 581 | name: inventory 582 | spec: 583 | port: 584 | targetPort: 8080 585 | to: 586 | kind: Service 587 | name: inventory 588 | weight: 100 589 | - apiVersion: v1 590 | kind: Route 591 | metadata: 592 | labels: 593 | app: gateway 594 | name: gateway 595 | spec: 596 | port: 597 | targetPort: 8080 598 | to: 599 | kind: Service 600 | name: gateway 601 | weight: 100 602 | - apiVersion: v1 603 | kind: Route 604 | metadata: 605 | labels: 606 | app: web 607 | name: web 608 | spec: 609 | port: 610 | targetPort: 8080 611 | tls: 612 | insecureEdgeTerminationPolicy: Allow 613 | termination: edge 614 | to: 615 | kind: Service 616 | name: web 617 | weight: 100 618 | - apiVersion: v1 619 | kind: RoleBinding 620 | metadata: 621 | name: view 622 | labels: 623 | app: coolstore 624 | roleRef: 625 | name: view 626 | subjects: 627 | - kind: ServiceAccount 628 | name: default 629 | - apiVersion: v1 630 | data: 631 | application.properties: |- 632 | spring.datasource.url=jdbc:postgresql://catalog-postgresql:5432/catalog 633 | spring.datasource.username=catalog 634 | spring.datasource.password=catalog 635 | spring.datasource.driver-class-name=org.postgresql.Driver 636 | spring.jpa.hibernate.ddl-auto=create 637 | kind: ConfigMap 638 | metadata: 639 | name: catalog 640 | labels: 641 | app: catalog 642 | - apiVersion: v1 643 | data: 644 | project-stages.yml: |- 645 | project: 646 | stage: prod 647 | swarm: 648 | datasources: 649 | data-sources: 650 | InventoryDS: 651 | driver-name: postgresql 652 | connection-url: jdbc:postgresql://inventory-postgresql:5432/inventory 653 | user-name: inventory 654 | password: inventory 655 | kind: ConfigMap 656 | metadata: 657 | name: inventory 658 | labels: 659 | app: inventory 660 | - apiVersion: v1 661 | kind: Secret 662 | metadata: 663 | name: inventory 664 | labels: 665 | app: inventory 666 | stringData: 667 | database-password: inventory 668 | database-user: inventory 669 | - apiVersion: v1 670 | kind: Secret 671 | metadata: 672 | name: catalog 673 | labels: 674 | app: catalog 675 | stringData: 676 | database-password: catalog 677 | database-user: catalog 678 | - apiVersion: v1 679 | kind: DeploymentConfig 680 | metadata: 681 | labels: 682 | app: inventory 683 | name: inventory-postgresql 684 | spec: 685 | replicas: 1 686 | selector: 687 | name: inventory-postgresql 688 | deploymentconfig: inventory-postgresql 689 | strategy: 690 | activeDeadlineSeconds: 21600 691 | recreateParams: 692 | timeoutSeconds: 600 693 | resources: {} 694 | type: Recreate 695 | template: 696 | metadata: 697 | labels: 698 | app: coolstore 699 | name: inventory-postgresql 700 | deploymentconfig: inventory-postgresql 701 | spec: 702 | containers: 703 | - env: 704 | - name: POSTGRESQL_USER 705 | valueFrom: 706 | secretKeyRef: 707 | key: database-user 708 | name: inventory 709 | - name: POSTGRESQL_PASSWORD 710 | valueFrom: 711 | secretKeyRef: 712 | key: database-password 713 | name: inventory 714 | - name: POSTGRESQL_DATABASE 715 | value: inventory 716 | image: postgresql 717 | imagePullPolicy: IfNotPresent 718 | livenessProbe: 719 | failureThreshold: 3 720 | initialDelaySeconds: 30 721 | periodSeconds: 10 722 | successThreshold: 1 723 | tcpSocket: 724 | port: 5432 725 | timeoutSeconds: 1 726 | name: postgresql 727 | ports: 728 | - containerPort: 5432 729 | protocol: TCP 730 | readinessProbe: 731 | exec: 732 | command: 733 | - /bin/sh 734 | - -i 735 | - -c 736 | - psql -h 127.0.0.1 -U $POSTGRESQL_USER -q -d $POSTGRESQL_DATABASE -c 737 | 'SELECT 1' 738 | failureThreshold: 3 739 | initialDelaySeconds: 5 740 | periodSeconds: 10 741 | successThreshold: 1 742 | timeoutSeconds: 1 743 | resources: 744 | limits: 745 | memory: 512Mi 746 | terminationMessagePath: /dev/termination-log 747 | terminationMessagePolicy: File 748 | volumeMounts: 749 | - mountPath: /var/lib/pgsql/data 750 | name: inventory-postgresql-data 751 | dnsPolicy: ClusterFirst 752 | restartPolicy: Always 753 | terminationGracePeriodSeconds: 30 754 | volumes: 755 | - emptyDir: {} 756 | name: inventory-postgresql-data 757 | triggers: 758 | - imageChangeParams: 759 | automatic: true 760 | containerNames: 761 | - postgresql 762 | from: 763 | kind: ImageStreamTag 764 | name: postgresql:9.5 765 | namespace: openshift 766 | type: ImageChange 767 | - type: ConfigChange 768 | - apiVersion: v1 769 | kind: Service 770 | metadata: 771 | labels: 772 | app: inventory 773 | name: inventory-postgresql 774 | spec: 775 | ports: 776 | - port: 5432 777 | protocol: TCP 778 | targetPort: 5432 779 | selector: 780 | deploymentconfig: inventory-postgresql 781 | type: ClusterIP 782 | - apiVersion: v1 783 | kind: DeploymentConfig 784 | metadata: 785 | labels: 786 | app: catalog 787 | name: catalog-postgresql 788 | spec: 789 | replicas: 1 790 | selector: 791 | name: catalog-postgresql 792 | deploymentconfig: catalog-postgresql 793 | strategy: 794 | activeDeadlineSeconds: 21600 795 | recreateParams: 796 | timeoutSeconds: 600 797 | resources: {} 798 | type: Recreate 799 | template: 800 | metadata: 801 | labels: 802 | name: catalog-postgresql 803 | deploymentconfig: catalog-postgresql 804 | app: coolstore 805 | spec: 806 | containers: 807 | - env: 808 | - name: POSTGRESQL_USER 809 | valueFrom: 810 | secretKeyRef: 811 | key: database-user 812 | name: catalog 813 | - name: POSTGRESQL_PASSWORD 814 | valueFrom: 815 | secretKeyRef: 816 | key: database-password 817 | name: catalog 818 | - name: POSTGRESQL_DATABASE 819 | value: catalog 820 | image: postgresql 821 | imagePullPolicy: IfNotPresent 822 | livenessProbe: 823 | failureThreshold: 3 824 | initialDelaySeconds: 30 825 | periodSeconds: 10 826 | successThreshold: 1 827 | tcpSocket: 828 | port: 5432 829 | timeoutSeconds: 1 830 | name: postgresql 831 | ports: 832 | - containerPort: 5432 833 | protocol: TCP 834 | readinessProbe: 835 | exec: 836 | command: 837 | - /bin/sh 838 | - -i 839 | - -c 840 | - psql -h 127.0.0.1 -U $POSTGRESQL_USER -q -d $POSTGRESQL_DATABASE -c 841 | 'SELECT 1' 842 | failureThreshold: 3 843 | initialDelaySeconds: 5 844 | periodSeconds: 10 845 | successThreshold: 1 846 | timeoutSeconds: 1 847 | resources: 848 | limits: 849 | memory: 512Mi 850 | terminationMessagePath: /dev/termination-log 851 | terminationMessagePolicy: File 852 | volumeMounts: 853 | - mountPath: /var/lib/pgsql/data 854 | name: catalog-postgresql-data 855 | dnsPolicy: ClusterFirst 856 | restartPolicy: Always 857 | terminationGracePeriodSeconds: 30 858 | volumes: 859 | - emptyDir: {} 860 | name: catalog-postgresql-data 861 | triggers: 862 | - imageChangeParams: 863 | automatic: true 864 | containerNames: 865 | - postgresql 866 | from: 867 | kind: ImageStreamTag 868 | name: postgresql:9.5 869 | namespace: openshift 870 | type: ImageChange 871 | - type: ConfigChange 872 | - apiVersion: v1 873 | kind: Service 874 | metadata: 875 | labels: 876 | app: catalog 877 | name: catalog-postgresql 878 | spec: 879 | ports: 880 | - port: 5432 881 | protocol: TCP 882 | targetPort: 5432 883 | selector: 884 | deploymentconfig: catalog-postgresql 885 | type: ClusterIP 886 | - apiVersion: v1 887 | kind: BuildConfig 888 | metadata: 889 | labels: 890 | app: inventory 891 | name: inventory-pipeline 892 | spec: 893 | runPolicy: Parallel 894 | source: 895 | contextDir: solutions/all/inventory-wildfly-swarm 896 | git: 897 | ref: ${GIT_REF} 898 | uri: ${GIT_URI} 899 | type: Git 900 | strategy: 901 | jenkinsPipelineStrategy: 902 | jenkinsfilePath: Jenkinsfile 903 | type: JenkinsPipeline 904 | triggers: 905 | - github: 906 | secret: V7l7DtTdDOaU3eioZb97 907 | type: GitHub 908 | - generic: 909 | secret: KyDr2_YFsWMsOjaWuzw_ 910 | type: Generic 911 | - type: ConfigChange 912 | -------------------------------------------------------------------------------- /solutions/all/gateway-vertx/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.iml 3 | *.log 4 | *.class 5 | tmp/ 6 | .idea/ 7 | .openshiftio 8 | .settings/ 9 | .classpath 10 | .project 11 | .DS_Store 12 | bower_components/ 13 | node_modules/ -------------------------------------------------------------------------------- /solutions/all/gateway-vertx/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.redhat.cloudnative 7 | gateway 8 | 1.0-SNAPSHOT 9 | jar 10 | CoolStore Gateway Service 11 | CoolStore Gateway Service with Eclipse Vert.x 12 | 13 | 3.5.1.redhat-004 14 | 1.0.15 15 | com.redhat.cloudnative.gateway.GatewayVerticle 16 | 3.5.40 17 | 1.7.21 18 | 19 | 20 | 21 | redhat-ga 22 | https://maven.repository.redhat.com/ga/ 23 | 24 | 25 | 26 | 27 | redhat-ga-plugins 28 | https://maven.repository.redhat.com/ga/ 29 | 30 | 31 | 32 | 33 | 34 | io.vertx 35 | vertx-dependencies 36 | ${vertx.version} 37 | pom 38 | import 39 | 40 | 41 | 42 | 43 | 44 | io.vertx 45 | vertx-core 46 | 47 | 48 | io.vertx 49 | vertx-web 50 | 51 | 52 | io.vertx 53 | vertx-circuit-breaker 54 | 55 | 56 | io.vertx 57 | vertx-web-client 58 | 59 | 60 | io.vertx 61 | vertx-rx-java 62 | 63 | 64 | io.vertx 65 | vertx-service-discovery 66 | 67 | 68 | io.vertx 69 | vertx-service-discovery-bridge-kubernetes 70 | 71 | 72 | org.slf4j 73 | slf4j-api 74 | ${slf4j.version} 75 | 76 | 77 | org.slf4j 78 | slf4j-jdk14 79 | ${slf4j.version} 80 | 81 | 82 | 83 | 84 | 85 | org.apache.maven.plugins 86 | maven-compiler-plugin 87 | 3.6.1 88 | 89 | 1.8 90 | 1.8 91 | 92 | 93 | 94 | io.reactiverse 95 | vertx-maven-plugin 96 | ${vertx-maven-plugin.version} 97 | 98 | 99 | vmp 100 | 101 | initialize 102 | package 103 | 104 | 105 | 106 | 107 | true 108 | -Djava.net.preferIPv4Stack=true 109 | 110 | 111 | 112 | io.fabric8 113 | fabric8-maven-plugin 114 | ${fabric8.maven.plugin.version} 115 | 116 | 117 | install 118 | 119 | resource 120 | build 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | app 130 | gateway 131 | 132 | 133 | 134 | 135 | 136 | 137 | vertx 138 | 139 | 140 | 141 | registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift:1.4 142 | 143 | 144 | 145 | 146 | 147 | 148 | /health 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /solutions/all/gateway-vertx/src/main/java/com/redhat/cloudnative/gateway/GatewayVerticle.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.gateway; 2 | 3 | 4 | import io.vertx.circuitbreaker.CircuitBreakerOptions; 5 | import io.vertx.core.http.HttpMethod; 6 | import io.vertx.core.json.Json; 7 | import io.vertx.core.json.JsonObject; 8 | import io.vertx.ext.web.client.WebClientOptions; 9 | import io.vertx.rxjava.circuitbreaker.CircuitBreaker; 10 | import io.vertx.rxjava.core.AbstractVerticle; 11 | import io.vertx.rxjava.ext.web.Router; 12 | import io.vertx.rxjava.ext.web.RoutingContext; 13 | import io.vertx.rxjava.ext.web.client.WebClient; 14 | import io.vertx.rxjava.ext.web.codec.BodyCodec; 15 | import io.vertx.rxjava.ext.web.handler.CorsHandler; 16 | import io.vertx.rxjava.servicediscovery.ServiceDiscovery; 17 | import io.vertx.rxjava.servicediscovery.types.HttpEndpoint; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import rx.Observable; 21 | import rx.Single; 22 | 23 | public class GatewayVerticle extends AbstractVerticle { 24 | private static final Logger LOG = LoggerFactory.getLogger(GatewayVerticle.class); 25 | 26 | private WebClient catalog; 27 | private WebClient inventory; 28 | private CircuitBreaker circuit; 29 | 30 | @Override 31 | public void start() { 32 | 33 | circuit = CircuitBreaker.create("inventory-circuit-breaker", vertx, 34 | new CircuitBreakerOptions() 35 | .setFallbackOnFailure(true) 36 | .setMaxFailures(3) 37 | .setResetTimeout(5000) 38 | .setTimeout(1000) 39 | ); 40 | 41 | Router router = Router.router(vertx); 42 | router.route().handler(CorsHandler.create("*").allowedMethod(HttpMethod.GET)); 43 | router.get("/health").handler(ctx -> ctx.response().end(new JsonObject().put("status", "UP").toString())); 44 | router.get("/api/products").handler(this::products); 45 | 46 | ServiceDiscovery.create(vertx, discovery -> { 47 | // Catalog lookup 48 | Single catalogDiscoveryRequest = HttpEndpoint.rxGetWebClient(discovery, 49 | rec -> rec.getName().equals("catalog")) 50 | .onErrorReturn(t -> WebClient.create(vertx, new WebClientOptions() 51 | .setDefaultHost(System.getProperty("catalog.api.host", "localhost")) 52 | .setDefaultPort(Integer.getInteger("catalog.api.port", 9000)))); 53 | 54 | // Inventory lookup 55 | Single inventoryDiscoveryRequest = HttpEndpoint.rxGetWebClient(discovery, 56 | rec -> rec.getName().equals("inventory")) 57 | .onErrorReturn(t -> WebClient.create(vertx, new WebClientOptions() 58 | .setDefaultHost(System.getProperty("inventory.api.host", "localhost")) 59 | .setDefaultPort(Integer.getInteger("inventory.api.port", 9001)))); 60 | 61 | // Zip all 3 requests 62 | Single.zip(catalogDiscoveryRequest, inventoryDiscoveryRequest, (c, i) -> { 63 | // When everything is done 64 | catalog = c; 65 | inventory = i; 66 | return vertx.createHttpServer() 67 | .requestHandler(router::accept) 68 | .listen(Integer.getInteger("http.port", 8080)); 69 | }).subscribe(); 70 | }); 71 | } 72 | 73 | private void products(RoutingContext rc) { 74 | // Retrieve catalog 75 | catalog.get("/api/catalog").as(BodyCodec.jsonArray()).rxSend() 76 | .map(resp -> { 77 | if (resp.statusCode() != 200) { 78 | new RuntimeException("Invalid response from the catalog: " + resp.statusCode()); 79 | } 80 | return resp.body(); 81 | }) 82 | .flatMap(products -> 83 | // For each item from the catalog, invoke the inventory service 84 | Observable.from(products) 85 | .cast(JsonObject.class) 86 | .flatMapSingle(product -> 87 | circuit.rxExecuteCommandWithFallback( 88 | future -> 89 | inventory.get("/api/inventory/" + product.getString("itemId")).as(BodyCodec.jsonObject()) 90 | .rxSend() 91 | .map(resp -> { 92 | if (resp.statusCode() != 200) { 93 | LOG.warn("Inventory error for {}: status code {}", 94 | product.getString("itemId"), resp.statusCode()); 95 | return product.copy(); 96 | } 97 | return product.copy().put("availability", 98 | new JsonObject().put("quantity", resp.body().getInteger("quantity"))); 99 | }) 100 | .subscribe( 101 | future::complete, 102 | future::fail), 103 | error -> { 104 | LOG.error("Inventory error for {}: {}", product.getString("itemId"), error.getMessage()); 105 | return product; 106 | } 107 | )) 108 | .toList().toSingle() 109 | ) 110 | .subscribe( 111 | list -> rc.response().end(Json.encodePrettily(list)), 112 | error -> rc.response().end(new JsonObject().put("error", error.getMessage()).toString()) 113 | ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /solutions/all/gateway-vertx/src/main/resources/vertx-default-jul-logging.properties: -------------------------------------------------------------------------------- 1 | 2 | handlers=java.util.logging.ConsoleHandler 3 | java.util.logging.SimpleFormatter.format=[%p] %t: %m%n\n 4 | java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter 5 | java.util.logging.ConsoleHandler.level=FINEST 6 | 7 | .level=INFO 8 | io.vertx.ext.web.level=INFO 9 | io.vertx.level=INFO 10 | com.hazelcast.level=INFO 11 | io.netty.util.internal.PlatformDependent.level=SEVERE 12 | io.vertx.servicediscovery.level=SEVERE 13 | -------------------------------------------------------------------------------- /solutions/all/inventory-wildfly-swarm/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.iml 3 | *.log 4 | *.class 5 | tmp/ 6 | .idea/ 7 | .openshiftio 8 | .settings/ 9 | .classpath 10 | .project 11 | .DS_Store 12 | bower_components/ 13 | node_modules/ -------------------------------------------------------------------------------- /solutions/all/inventory-wildfly-swarm/Jenkinsfile: -------------------------------------------------------------------------------- 1 | node("maven") { 2 | stage("Build JAR") { 3 | git url: "https://github.com/siamaksade/demo-inventory.git" 4 | sh "mvn clean package" 5 | stash name:"jar", includes:"target/inventory-1.0-SNAPSHOT-swarm.jar" 6 | } 7 | 8 | stage("Build Image") { 9 | unstash name:"jar" 10 | sh "oc start-build inventory-s2i --from-file=target/inventory-1.0-SNAPSHOT-swarm.jar" 11 | openshiftVerifyBuild bldCfg: "inventory-s2i", waitTime: "20", waitUnit: "min" 12 | } 13 | 14 | stage("Deploy") { 15 | openshiftDeploy depCfg: "inventory" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /solutions/all/inventory-wildfly-swarm/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.redhat.cloudnative 7 | inventory 8 | 1.0-SNAPSHOT 9 | war 10 | CoolStore Inventory Service 11 | CoolStore Inventory Service with WildFly Swarm 12 | 13 | 7.1.0.redhat-77 14 | 3.0.12 15 | 3.5.40 16 | 1.4.187 17 | 42.2.4 18 | false 19 | 20 | 21 | 22 | redhat-ga 23 | https://maven.repository.redhat.com/ga/ 24 | 25 | 26 | 27 | 28 | redhat-ga-plugins 29 | https://maven.repository.redhat.com/ga/ 30 | 31 | 32 | 33 | 34 | 35 | org.wildfly.swarm 36 | bom 37 | ${version.wildfly.swarm} 38 | pom 39 | import 40 | 41 | 42 | org.wildfly.swarm 43 | bom-certified 44 | ${version.wildfly.swarm} 45 | pom 46 | import 47 | 48 | 49 | io.fabric8 50 | fabric8-project-bom-with-platform-deps 51 | ${fabric8.version} 52 | pom 53 | import 54 | 55 | 56 | 57 | 58 | 59 | org.wildfly.swarm 60 | jaxrs-jsonp 61 | 62 | 63 | org.wildfly.swarm 64 | cdi 65 | 66 | 67 | org.wildfly.swarm 68 | jpa 69 | 70 | 71 | org.wildfly.swarm 72 | monitor 73 | 74 | 75 | com.h2database 76 | h2 77 | ${h2.version} 78 | 79 | 80 | org.postgresql 81 | postgresql 82 | ${postgresql.version} 83 | 84 | 85 | 86 | 87 | 88 | org.apache.maven.plugins 89 | maven-compiler-plugin 90 | 3.6.1 91 | 92 | 1.8 93 | 1.8 94 | 95 | 96 | 97 | maven-war-plugin 98 | 3.1.0 99 | 100 | false 101 | 102 | 103 | 104 | org.wildfly.swarm 105 | wildfly-swarm-plugin 106 | ${version.wildfly.swarm} 107 | 108 | 109 | 110 | package 111 | 112 | 113 | 114 | 115 | 116 | true 117 | 118 | 119 | -Dswarm.http.port=9001 120 | 121 | 122 | 123 | 124 | io.fabric8 125 | fabric8-maven-plugin 126 | ${fabric8.maven.plugin.version} 127 | 128 | 129 | install 130 | 131 | resource 132 | build 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | app 142 | inventory 143 | 144 | 145 | 146 | 147 | 300 148 | 149 | 150 | 151 | 152 | wildfly-swarm 153 | 154 | 155 | 156 | registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift:1.4 157 | 158 | 159 | 160 | 161 | 162 | 163 | /node 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /solutions/all/inventory-wildfly-swarm/src/main/java/com/redhat/cloudnative/inventory/Inventory.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.inventory; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.Id; 5 | import javax.persistence.Table; 6 | import javax.persistence.UniqueConstraint; 7 | import java.io.Serializable; 8 | 9 | @Entity 10 | @Table(name = "INVENTORY", uniqueConstraints = @UniqueConstraint(columnNames = "itemId")) 11 | public class Inventory implements Serializable { 12 | private static final long serialVersionUID = -8053933344541613739L; 13 | 14 | @Id 15 | private String itemId; 16 | 17 | private int quantity; 18 | 19 | public Inventory() { 20 | } 21 | 22 | public String getItemId() { 23 | return itemId; 24 | } 25 | 26 | public void setItemId(String itemId) { 27 | this.itemId = itemId; 28 | } 29 | 30 | public int getQuantity() { 31 | return quantity; 32 | } 33 | 34 | public void setQuantity(int quantity) { 35 | this.quantity = quantity; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return "Inventory [" + 41 | "itemId='" + itemId + '\'' + 42 | ", quantity=" + quantity + 43 | ']'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /solutions/all/inventory-wildfly-swarm/src/main/java/com/redhat/cloudnative/inventory/InventoryApplication.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.inventory; 2 | 3 | import javax.ws.rs.ApplicationPath; 4 | import javax.ws.rs.core.Application; 5 | 6 | @ApplicationPath("/") 7 | public class InventoryApplication extends Application { 8 | } 9 | -------------------------------------------------------------------------------- /solutions/all/inventory-wildfly-swarm/src/main/java/com/redhat/cloudnative/inventory/InventoryResource.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.inventory; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.persistence.EntityManager; 5 | import javax.persistence.PersistenceContext; 6 | import javax.ws.rs.GET; 7 | import javax.ws.rs.Path; 8 | import javax.ws.rs.PathParam; 9 | import javax.ws.rs.Produces; 10 | import javax.ws.rs.core.MediaType; 11 | import org.wildfly.swarm.health.Health; 12 | import org.wildfly.swarm.health.HealthStatus; 13 | import java.util.Date; 14 | 15 | @Path("/") 16 | @ApplicationScoped 17 | public class InventoryResource { 18 | @PersistenceContext(unitName = "InventoryPU") 19 | private EntityManager em; 20 | 21 | @GET 22 | @Path("/api/inventory/{itemId}") 23 | @Produces(MediaType.APPLICATION_JSON) 24 | public Inventory getAvailability(@PathParam("itemId") String itemId) { 25 | Inventory inventory = em.find(Inventory.class, itemId); 26 | 27 | if (inventory == null) { 28 | inventory = new Inventory(); 29 | inventory.setItemId(itemId); 30 | inventory.setQuantity(0); 31 | } 32 | 33 | return inventory; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /solutions/all/inventory-wildfly-swarm/src/main/resources/META-INF/load.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO INVENTORY(itemId, quantity) VALUES (329299, 35) 2 | INSERT INTO INVENTORY(itemId, quantity) VALUES (329199, 12) 3 | INSERT INTO INVENTORY(itemId, quantity) VALUES (165613, 45) 4 | INSERT INTO INVENTORY(itemId, quantity) VALUES (165614, 87) 5 | INSERT INTO INVENTORY(itemId, quantity) VALUES (165954, 43) 6 | INSERT INTO INVENTORY(itemId, quantity) VALUES (444434, 32) 7 | INSERT INTO INVENTORY(itemId, quantity) VALUES (444435, 53) 8 | -------------------------------------------------------------------------------- /solutions/all/inventory-wildfly-swarm/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 23 | 24 | java:/jboss/datasources/InventoryDS 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /solutions/all/inventory-wildfly-swarm/src/main/resources/project-defaults.yml: -------------------------------------------------------------------------------- 1 | swarm: 2 | datasources: 3 | data-sources: 4 | InventoryDS: 5 | driver-name: h2 6 | connection-url: jdbc:h2:mem:fruits;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE 7 | user-name: sa 8 | password: sa -------------------------------------------------------------------------------- /solutions/all/kill-mvn-process.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PROCESSES=$(ps aux | grep apache-maven | awk -F' ' '{print $2}') 4 | 5 | for p in $PROCESSES ; do 6 | kill -9 $p 2>/dev/null 7 | done -------------------------------------------------------------------------------- /solutions/catalog-spring-boot/CatalogController.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.catalog; 2 | 3 | import java.util.*; 4 | import java.util.stream.*; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.MediaType; 7 | import org.springframework.stereotype.Controller; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | @Controller 11 | @RequestMapping(value = "/api/catalog") 12 | public class CatalogController { 13 | @Autowired 14 | private ProductRepository repository; 15 | 16 | @ResponseBody 17 | @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) 18 | public List getAll() { 19 | Spliterator products = repository.findAll().spliterator(); 20 | return StreamSupport.stream(products, false).collect(Collectors.toList()); 21 | } 22 | } -------------------------------------------------------------------------------- /solutions/catalog-spring-boot/Product.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.catalog; 2 | 3 | import java.io.Serializable; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.Id; 7 | import javax.persistence.Table; 8 | import javax.persistence.UniqueConstraint; 9 | 10 | @Entity 11 | @Table(name = "PRODUCT", uniqueConstraints = @UniqueConstraint(columnNames = "itemId")) 12 | public class Product implements Serializable { 13 | 14 | @Id 15 | private String itemId; 16 | 17 | private String name; 18 | 19 | private String description; 20 | 21 | private double price; 22 | 23 | public Product() { 24 | } 25 | 26 | public String getItemId() { 27 | return itemId; 28 | } 29 | 30 | public void setItemId(String itemId) { 31 | this.itemId = itemId; 32 | } 33 | 34 | public String getName() { 35 | return name; 36 | } 37 | 38 | public void setName(String name) { 39 | this.name = name; 40 | } 41 | 42 | public String getDescription() { 43 | return description; 44 | } 45 | 46 | public void setDescription(String description) { 47 | this.description = description; 48 | } 49 | 50 | public double getPrice() { 51 | return price; 52 | } 53 | 54 | public void setPrice(double price) { 55 | this.price = price; 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return "Product [itemId=" + itemId + ", name=" + name + ", price=" + price + "]"; 61 | } 62 | } -------------------------------------------------------------------------------- /solutions/catalog-spring-boot/ProductRepository.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.catalog; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | public interface ProductRepository extends CrudRepository { 6 | } -------------------------------------------------------------------------------- /solutions/catalog-spring-boot/solve&deploy.sh: -------------------------------------------------------------------------------- 1 | ################################ 2 | # catalog-spring-boot Solution # 3 | ################################ 4 | 5 | DIRECTORY=`dirname $0` 6 | 7 | $DIRECTORY/solve.sh 8 | mvn clean fabric8:deploy -f $DIRECTORY/../../catalog-spring-boot -------------------------------------------------------------------------------- /solutions/catalog-spring-boot/solve.sh: -------------------------------------------------------------------------------- 1 | ################################ 2 | # catalog-spring-boot Solution # 3 | ################################ 4 | 5 | DIRECTORY=`dirname $0` 6 | 7 | cp $DIRECTORY/*.java $DIRECTORY/../../catalog-spring-boot/src/main/java/com/redhat/cloudnative/catalog -------------------------------------------------------------------------------- /solutions/fault-tolerance-gateway-vertx/GatewayVerticle.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.gateway; 2 | 3 | import io.vertx.circuitbreaker.CircuitBreakerOptions; 4 | import io.vertx.core.http.HttpMethod; 5 | import io.vertx.core.json.JsonArray; 6 | import io.vertx.core.json.JsonObject; 7 | import io.vertx.ext.web.client.WebClientOptions; 8 | import io.vertx.reactivex.circuitbreaker.CircuitBreaker; 9 | import io.vertx.reactivex.core.AbstractVerticle; 10 | import io.vertx.reactivex.ext.web.Router; 11 | import io.vertx.reactivex.ext.web.RoutingContext; 12 | import io.vertx.reactivex.ext.web.client.WebClient; 13 | import io.vertx.reactivex.ext.web.client.predicate.ResponsePredicate; 14 | import io.vertx.reactivex.ext.web.codec.BodyCodec; 15 | import io.vertx.reactivex.ext.web.handler.CorsHandler; 16 | import io.vertx.reactivex.ext.web.handler.StaticHandler; 17 | import io.vertx.reactivex.servicediscovery.ServiceDiscovery; 18 | import io.vertx.reactivex.servicediscovery.types.HttpEndpoint; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import io.reactivex.Observable; 22 | import io.reactivex.Single; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | public class GatewayVerticle extends AbstractVerticle { 28 | private static final Logger LOG = LoggerFactory.getLogger(GatewayVerticle.class); 29 | 30 | private WebClient catalog; 31 | private WebClient inventory; 32 | private CircuitBreaker circuit; 33 | 34 | @Override 35 | public void start() { 36 | 37 | circuit = CircuitBreaker.create("inventory-circuit-breaker", vertx, 38 | new CircuitBreakerOptions() 39 | .setFallbackOnFailure(true) 40 | .setMaxFailures(3) 41 | .setResetTimeout(5000) 42 | .setTimeout(1000) 43 | ); 44 | 45 | Router router = Router.router(vertx); 46 | router.route().handler(CorsHandler.create("*").allowedMethod(HttpMethod.GET)); 47 | router.get("/*").handler(StaticHandler.create("assets")); 48 | router.get("/health").handler(ctx -> ctx.response().end(new JsonObject().put("status", "UP").toString())); 49 | router.get("/api/products").handler(this::products); 50 | 51 | ServiceDiscovery.create(vertx, discovery -> { 52 | // Catalog lookup 53 | Single catalogDiscoveryRequest = HttpEndpoint.rxGetWebClient(discovery, 54 | rec -> rec.getName().equals("catalog")) 55 | .onErrorReturn(t -> WebClient.create(vertx, new WebClientOptions() 56 | .setDefaultHost(System.getProperty("catalog.api.host", "localhost")) 57 | .setDefaultPort(Integer.getInteger("catalog.api.port", 9000)))); 58 | 59 | // Inventory lookup 60 | Single inventoryDiscoveryRequest = HttpEndpoint.rxGetWebClient(discovery, 61 | rec -> rec.getName().equals("inventory")) 62 | .onErrorReturn(t -> WebClient.create(vertx, new WebClientOptions() 63 | .setDefaultHost(System.getProperty("inventory.api.host", "localhost")) 64 | .setDefaultPort(Integer.getInteger("inventory.api.port", 9001)))); 65 | 66 | // Zip all 3 requests 67 | Single.zip(catalogDiscoveryRequest, inventoryDiscoveryRequest, (c, i) -> { 68 | // When everything is done 69 | catalog = c; 70 | inventory = i; 71 | return vertx.createHttpServer() 72 | .requestHandler(router) 73 | .listen(Integer.getInteger("http.port", 8080)); 74 | }).subscribe(); 75 | }); 76 | } 77 | 78 | private void products(RoutingContext rc) { 79 | // Retrieve catalog 80 | catalog 81 | .get("/api/catalog") 82 | .expect(ResponsePredicate.SC_OK) 83 | .as(BodyCodec.jsonArray()) 84 | .rxSend() 85 | .map(resp -> { 86 | // Map the response to a list of JSON object 87 | List listOfProducts = new ArrayList<>(); 88 | for (Object product : resp.body()) { 89 | listOfProducts.add((JsonObject)product); 90 | } 91 | return listOfProducts; 92 | }) 93 | .flatMap(products -> { 94 | // For each item from the catalog, invoke the inventory service 95 | // and create a JsonArray containing all the results 96 | return Observable.fromIterable(products) 97 | .flatMapSingle(product -> 98 | circuit.rxExecuteCommandWithFallback( 99 | future -> 100 | getAvailabilityFromInventory(product).subscribe(future::complete, future::fail), 101 | error -> { 102 | LOG.warn("Inventory error for {}: status code {}", product.getString("itemId"), error); 103 | return product.copy(); 104 | }) 105 | ) 106 | .collect(JsonArray::new, JsonArray::add); 107 | } 108 | ) 109 | .subscribe( 110 | list -> rc.response().end(list.encodePrettily()), 111 | error -> rc.response().setStatusCode(500).end(new JsonObject().put("error", error.getMessage()).toString()) 112 | ); 113 | } 114 | 115 | private Single getAvailabilityFromInventory(JsonObject product) { 116 | // Retrieve the inventory for a given product 117 | return inventory 118 | .get("/api/inventory/" + product.getString("itemId")) 119 | .as(BodyCodec.jsonObject()) 120 | .rxSend() 121 | .map(resp -> { 122 | if (resp.statusCode() != 200) { 123 | LOG.warn("Inventory error for {}: status code {}", 124 | product.getString("itemId"), resp.statusCode()); 125 | return product.copy(); 126 | } 127 | return product.copy().put("availability", 128 | new JsonObject().put("quantity", resp.body().getInteger("quantity"))); 129 | }); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /solutions/gateway-vertx/GatewayVerticle.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.gateway; 2 | 3 | import io.vertx.core.http.HttpMethod; 4 | import io.vertx.core.json.JsonArray; 5 | import io.vertx.core.json.JsonObject; 6 | import io.vertx.ext.web.client.WebClientOptions; 7 | import io.vertx.reactivex.core.AbstractVerticle; 8 | import io.vertx.reactivex.ext.web.Router; 9 | import io.vertx.reactivex.ext.web.RoutingContext; 10 | import io.vertx.reactivex.ext.web.client.WebClient; 11 | import io.vertx.reactivex.ext.web.client.predicate.ResponsePredicate; 12 | import io.vertx.reactivex.ext.web.codec.BodyCodec; 13 | import io.vertx.reactivex.ext.web.handler.CorsHandler; 14 | import io.vertx.reactivex.ext.web.handler.StaticHandler; 15 | import io.vertx.reactivex.servicediscovery.ServiceDiscovery; 16 | import io.vertx.reactivex.servicediscovery.types.HttpEndpoint; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | import io.reactivex.Observable; 20 | import io.reactivex.Single; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | public class GatewayVerticle extends AbstractVerticle { 26 | private static final Logger LOG = LoggerFactory.getLogger(GatewayVerticle.class); 27 | 28 | private WebClient catalog; 29 | private WebClient inventory; 30 | 31 | @Override 32 | public void start() { 33 | Router router = Router.router(vertx); 34 | router.route().handler(CorsHandler.create("*").allowedMethod(HttpMethod.GET)); 35 | router.get("/*").handler(StaticHandler.create("assets")); 36 | router.get("/health").handler(ctx -> ctx.response().end(new JsonObject().put("status", "UP").toString())); 37 | router.get("/api/products").handler(this::products); 38 | 39 | ServiceDiscovery.create(vertx, discovery -> { 40 | // Catalog lookup 41 | Single catalogDiscoveryRequest = HttpEndpoint.rxGetWebClient(discovery, 42 | rec -> rec.getName().equals("catalog")) 43 | .onErrorReturn(t -> WebClient.create(vertx, new WebClientOptions() 44 | .setDefaultHost(System.getProperty("catalog.api.host", "localhost")) 45 | .setDefaultPort(Integer.getInteger("catalog.api.port", 9000)))); 46 | 47 | // Inventory lookup 48 | Single inventoryDiscoveryRequest = HttpEndpoint.rxGetWebClient(discovery, 49 | rec -> rec.getName().equals("inventory")) 50 | .onErrorReturn(t -> WebClient.create(vertx, new WebClientOptions() 51 | .setDefaultHost(System.getProperty("inventory.api.host", "localhost")) 52 | .setDefaultPort(Integer.getInteger("inventory.api.port", 9001)))); 53 | 54 | // Zip all 3 requests 55 | Single.zip(catalogDiscoveryRequest, inventoryDiscoveryRequest, (c, i) -> { 56 | // When everything is done 57 | catalog = c; 58 | inventory = i; 59 | return vertx.createHttpServer() 60 | .requestHandler(router) 61 | .listen(Integer.getInteger("http.port", 8080)); 62 | }).subscribe(); 63 | }); 64 | } 65 | 66 | private void products(RoutingContext rc) { 67 | // Retrieve catalog 68 | catalog 69 | .get("/api/catalog") 70 | .expect(ResponsePredicate.SC_OK) 71 | .as(BodyCodec.jsonArray()) 72 | .rxSend() 73 | .map(resp -> { 74 | // Map the response to a list of JSON object 75 | List listOfProducts = new ArrayList<>(); 76 | for (Object product : resp.body()) { 77 | listOfProducts.add((JsonObject)product); 78 | } 79 | return listOfProducts; 80 | }) 81 | .flatMap(products -> { 82 | // For each item from the catalog, invoke the inventory service 83 | // and create a JsonArray containing all the results 84 | return Observable.fromIterable(products) 85 | .flatMapSingle(this::getAvailabilityFromInventory) 86 | .collect(JsonArray::new, JsonArray::add); 87 | } 88 | ) 89 | .subscribe( 90 | list -> rc.response().end(list.encodePrettily()), 91 | error -> rc.response().setStatusCode(500).end(new JsonObject().put("error", error.getMessage()).toString()) 92 | ); 93 | } 94 | 95 | private Single getAvailabilityFromInventory(JsonObject product) { 96 | // Retrieve the inventory for a given product 97 | return inventory 98 | .get("/api/inventory/" + product.getString("itemId")) 99 | .as(BodyCodec.jsonObject()) 100 | .rxSend() 101 | .map(resp -> { 102 | if (resp.statusCode() != 200) { 103 | LOG.warn("Inventory error for {}: status code {}", 104 | product.getString("itemId"), resp.statusCode()); 105 | return product.copy(); 106 | } 107 | return product.copy().put("availability", 108 | new JsonObject().put("quantity", resp.body().getInteger("quantity"))); 109 | }); 110 | } 111 | } -------------------------------------------------------------------------------- /solutions/gateway-vertx/solve&deploy.sh: -------------------------------------------------------------------------------- 1 | ########################## 2 | # gateway-vertx Solution # 3 | ########################## 4 | 5 | DIRECTORY=`dirname $0` 6 | 7 | $DIRECTORY/solve.sh 8 | mvn clean fabric8:deploy -f $DIRECTORY/../../gateway-vertx -------------------------------------------------------------------------------- /solutions/gateway-vertx/solve.sh: -------------------------------------------------------------------------------- 1 | ########################## 2 | # gateway-vertx Solution # 3 | ########################## 4 | 5 | DIRECTORY=`dirname $0` 6 | 7 | cp $DIRECTORY/*.java $DIRECTORY/../../gateway-vertx/src/main/java/com/redhat/cloudnative/gateway 8 | -------------------------------------------------------------------------------- /solutions/inventory-thorntail/Inventory.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.inventory; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.Id; 5 | import javax.persistence.Table; 6 | import javax.persistence.UniqueConstraint; 7 | import java.io.Serializable; 8 | 9 | @Entity 10 | @Table(name = "INVENTORY", uniqueConstraints = @UniqueConstraint(columnNames = "itemId")) 11 | public class Inventory implements Serializable { 12 | @Id 13 | private String itemId; 14 | 15 | private int quantity; 16 | 17 | public Inventory() { 18 | } 19 | 20 | public String getItemId() { 21 | return itemId; 22 | } 23 | 24 | public void setItemId(String itemId) { 25 | this.itemId = itemId; 26 | } 27 | 28 | public int getQuantity() { 29 | return quantity; 30 | } 31 | 32 | public void setQuantity(int quantity) { 33 | this.quantity = quantity; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "Inventory [itemId='" + itemId + '\'' + ", quantity=" + quantity + ']'; 39 | } 40 | } -------------------------------------------------------------------------------- /solutions/inventory-thorntail/InventoryResource.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.inventory; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.persistence.*; 5 | import javax.ws.rs.*; 6 | import javax.ws.rs.core.MediaType; 7 | 8 | @Path("/inventory") 9 | @ApplicationScoped 10 | public class InventoryResource { 11 | @PersistenceContext(unitName = "InventoryPU") 12 | private EntityManager em; 13 | 14 | @GET 15 | @Path("/{itemId}") 16 | @Produces(MediaType.APPLICATION_JSON) 17 | public Inventory getAvailability(@PathParam("itemId") String itemId) { 18 | Inventory inventory = em.find(Inventory.class, itemId); 19 | return inventory; 20 | } 21 | } -------------------------------------------------------------------------------- /solutions/inventory-thorntail/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.redhat.cloudnative 7 | inventory 8 | 1.0-SNAPSHOT 9 | war 10 | CoolStore Inventory Service 11 | CoolStore Inventory Service with Thorntail 12 | 13 | 2.2.0.Final-redhat-00021 14 | 3.0.12 15 | 3.5.40 16 | 1.4.187 17 | 42.2.4 18 | false 19 | 20 | 21 | 22 | redhat-ga 23 | https://maven.repository.redhat.com/ga/ 24 | 25 | 26 | 27 | 28 | redhat-ga-plugins 29 | https://maven.repository.redhat.com/ga/ 30 | 31 | 32 | 33 | 34 | 35 | io.thorntail 36 | bom 37 | ${version.thorntail} 38 | pom 39 | import 40 | 41 | 42 | io.fabric8 43 | fabric8-project-bom-with-platform-deps 44 | ${fabric8.version} 45 | pom 46 | import 47 | 48 | 49 | 50 | 51 | 52 | io.thorntail 53 | jaxrs-jsonp 54 | 55 | 56 | io.thorntail 57 | cdi 58 | 59 | 60 | io.thorntail 61 | jpa 62 | 63 | 64 | io.thorntail 65 | microprofile-restclient 66 | 67 | 68 | io.thorntail 69 | microprofile-health 70 | 71 | 72 | com.h2database 73 | h2 74 | ${h2.version} 75 | 76 | 77 | org.postgresql 78 | postgresql 79 | ${postgresql.version} 80 | 81 | 82 | 83 | 84 | 85 | org.apache.maven.plugins 86 | maven-compiler-plugin 87 | 3.6.1 88 | 89 | 1.8 90 | 1.8 91 | 92 | 93 | 94 | maven-war-plugin 95 | 3.1.0 96 | 97 | false 98 | 99 | 100 | 101 | io.thorntail 102 | thorntail-maven-plugin 103 | ${version.thorntail} 104 | 105 | 106 | 107 | package 108 | 109 | 110 | 111 | 112 | 113 | true 114 | 115 | 116 | -Dthorntail.http.port=9001 117 | 118 | 119 | 120 | 121 | io.fabric8 122 | fabric8-maven-plugin 123 | ${fabric8.maven.plugin.version} 124 | 125 | 126 | install 127 | 128 | resource 129 | build 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | app 139 | inventory 140 | 141 | 142 | 143 | 144 | 300 145 | 146 | 147 | 148 | 149 | thorntail-v2 150 | 151 | 152 | 153 | registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift:1.4 154 | 155 | 156 | 157 | 158 | 159 | thorntail-v2-health-check 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /solutions/inventory-thorntail/solve&deploy.sh: -------------------------------------------------------------------------------- 1 | ################################ 2 | # inventory-thorntail Solution # 3 | ################################ 4 | 5 | DIRECTORY=`dirname $0` 6 | 7 | $DIRECTORY/solve.sh 8 | mvn clean fabric8:deploy -f $DIRECTORY/../../inventory-thorntail 9 | -------------------------------------------------------------------------------- /solutions/inventory-thorntail/solve.sh: -------------------------------------------------------------------------------- 1 | ################################ 2 | # inventory-thorntail Solution # 3 | ################################ 4 | 5 | DIRECTORY=`dirname $0` 6 | 7 | cp $DIRECTORY/*.java $DIRECTORY/../../inventory-thorntail/src/main/java/com/redhat/cloudnative/inventory 8 | cp $DIRECTORY/pom.xml $DIRECTORY/../../inventory-thorntail 9 | -------------------------------------------------------------------------------- /solutions/inventory-vertx/InventoryVerticle.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.inventory; 2 | 3 | import io.reactivex.Completable; 4 | import io.reactivex.Single; 5 | import io.vertx.reactivex.config.ConfigRetriever; 6 | import io.vertx.config.ConfigRetrieverOptions; 7 | import io.vertx.config.ConfigStoreOptions; 8 | import io.vertx.core.json.JsonArray; 9 | import io.vertx.core.json.JsonObject; 10 | import io.vertx.reactivex.core.http.HttpServer; 11 | import io.vertx.reactivex.ext.jdbc.JDBCClient; 12 | import io.vertx.ext.sql.ResultSet; 13 | import io.vertx.reactivex.ext.web.Router; 14 | import io.vertx.reactivex.core.AbstractVerticle; 15 | import io.vertx.reactivex.ext.web.RoutingContext; 16 | import io.vertx.reactivex.ext.web.handler.StaticHandler; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | 20 | import java.util.List; 21 | 22 | public class InventoryVerticle extends AbstractVerticle { 23 | 24 | private static final Logger LOG = LoggerFactory.getLogger(InventoryVerticle.class); 25 | 26 | private JDBCClient inventoryClient; 27 | 28 | @Override 29 | public Completable rxStart() { 30 | 31 | Router router = Router.router(vertx); 32 | router.get("/*").handler(StaticHandler.create("assets")); 33 | router.get("/health").handler(ctx -> ctx.response().end(new JsonObject().put("status", "UP").toString())); 34 | router.get("/api/inventory/:itemId").handler(this::findQuantity); 35 | 36 | Single serverSingle = vertx.createHttpServer() 37 | .requestHandler(router) 38 | .rxListen(Integer.getInteger("http.port", 8080)); 39 | 40 | ConfigRetrieverOptions configOptions = new ConfigRetrieverOptions() 41 | .addStore(new ConfigStoreOptions() 42 | .setType("file") 43 | .setFormat("yaml") 44 | .setConfig(new JsonObject() 45 | .put("path", "config/app-config.yml"))); 46 | ConfigRetriever retriever = ConfigRetriever.create(vertx, configOptions); 47 | Single s = retriever.rxGetConfig(); 48 | 49 | return s 50 | .flatMap(this::populateDatabase) 51 | .flatMap(rs -> serverSingle) 52 | .ignoreElement(); 53 | } 54 | 55 | private void findQuantity(RoutingContext rc) { 56 | String itemId = rc.pathParam("itemId"); 57 | inventoryClient.queryWithParams( 58 | "select \"QUANTITY\" from \"INVENTORY\" where \"ITEMID\"=?", 59 | new JsonArray().add(itemId), 60 | ar -> { 61 | if (ar.succeeded()) { 62 | ResultSet resultSet = ar.result(); 63 | List rows = resultSet.getRows(); 64 | if (rows.size() == 1) { 65 | int quantity = rows.get(0).getInteger("QUANTITY"); 66 | JsonObject body = new JsonObject() 67 | .put("quantity", quantity) 68 | .put("itemId", itemId); 69 | rc.response() 70 | .putHeader("content-type", "application/json") 71 | .end(body.encodePrettily()); 72 | } else { 73 | rc.response().setStatusCode(404).end("Product " + itemId + " not found"); 74 | } 75 | } else { 76 | LOG.error("Could not access database", ar); 77 | rc.fail(500, ar.cause()); 78 | } 79 | }); 80 | } 81 | 82 | private Single populateDatabase(JsonObject config) { 83 | LOG.info("Will use database " + config.getValue("jdbcUrl")); 84 | inventoryClient = JDBCClient.createNonShared(vertx, config); 85 | String sql = "" + 86 | "drop table if exists INVENTORY;" + 87 | "create table \"INVENTORY\" (\"ITEMID\" varchar(32) PRIMARY KEY, \"QUANTITY\" int);" + 88 | "insert into \"INVENTORY\" (\"ITEMID\", \"QUANTITY\") values (329299, 35);" + 89 | "insert into \"INVENTORY\" (\"ITEMID\", \"QUANTITY\") values (329199, 12);" + 90 | "insert into \"INVENTORY\" (\"ITEMID\", \"QUANTITY\") values (165613, 45);" + 91 | "insert into \"INVENTORY\" (\"ITEMID\", \"QUANTITY\") values (165614, 87);" + 92 | "insert into \"INVENTORY\" (\"ITEMID\", \"QUANTITY\") values (165954, 43);" + 93 | "insert into \"INVENTORY\" (\"ITEMID\", \"QUANTITY\") values (444434, 32);" + 94 | "insert into \"INVENTORY\" (\"ITEMID\", \"QUANTITY\") values (444435, 53);"; 95 | return inventoryClient.rxQuery(sql); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /solutions/inventory-vertx/solve.sh: -------------------------------------------------------------------------------- 1 | ################################# 2 | # Lab Inventory Vertx Solution # 3 | ################################# 4 | 5 | DIRECTORY=`dirname $0` 6 | 7 | # Deploy to OpenShift 8 | cp $DIRECTORY/*.java $DIRECTORY/../../inventory-vertx/src/main/java/com/redhat/cloudnative/inventory 9 | mvn clean fabric8:deploy -f $DIRECTORY/../../inventory-vertx -------------------------------------------------------------------------------- /solutions/lab-10/InventoryResource.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.inventory; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.persistence.EntityManager; 5 | import javax.persistence.PersistenceContext; 6 | import javax.ws.rs.GET; 7 | import javax.ws.rs.Path; 8 | import javax.ws.rs.PathParam; 9 | import javax.ws.rs.Produces; 10 | import javax.ws.rs.core.MediaType; 11 | import org.wildfly.swarm.health.Health; 12 | import org.wildfly.swarm.health.HealthStatus; 13 | import java.util.Date; 14 | 15 | @Path("/") 16 | @ApplicationScoped 17 | public class InventoryResource { 18 | @PersistenceContext(unitName = "InventoryPU") 19 | private EntityManager em; 20 | 21 | @GET 22 | @Path("/api/inventory/{itemId}") 23 | @Produces(MediaType.APPLICATION_JSON) 24 | public Inventory getAvailability(@PathParam("itemId") String itemId) { 25 | Inventory inventory = em.find(Inventory.class, itemId); 26 | 27 | if (inventory == null) { 28 | inventory = new Inventory(); 29 | inventory.setItemId(itemId); 30 | inventory.setQuantity(0); 31 | } 32 | 33 | return inventory; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /solutions/lab-10/solve&deploy.sh: -------------------------------------------------------------------------------- 1 | ################### 2 | # Lab 10 Solution # 3 | ################### 4 | 5 | DIRECTORY=`dirname $0` 6 | 7 | # Deploy to OpenShift 8 | $DIRECTORY/solve.sh 9 | mvn clean fabric8:deploy -f $DIRECTORY/../../inventory-wildfly-swarm -------------------------------------------------------------------------------- /solutions/lab-10/solve.sh: -------------------------------------------------------------------------------- 1 | ################### 2 | # Lab 10 Solution # 3 | ################### 4 | 5 | DIRECTORY=`dirname $0` 6 | 7 | cp $DIRECTORY/*.java $DIRECTORY/../../inventory-thorntail/src/main/java/com/redhat/cloudnative/inventory -------------------------------------------------------------------------------- /solutions/lab-5/solve.sh: -------------------------------------------------------------------------------- 1 | ################### 2 | # Lab 5 Solution # 3 | ################### 4 | 5 | # Deploy Web on OpenShift 6 | oc new-app nodejs~https://github.com/openshift-labs/cloud-native-labs.git#ocp-3.11 \ 7 | --context-dir=web-nodejs \ 8 | --name=web 9 | 10 | oc expose svc/web 11 | -------------------------------------------------------------------------------- /solutions/lab-6/solve.sh: -------------------------------------------------------------------------------- 1 | ################### 2 | # Lab 6 Solution # 3 | ################### 4 | 5 | # Health Probes 6 | oc set probe dc/catalog --liveness --readiness --initial-delay-seconds=30 --failure-threshold=3 --get-url=http://:8080/health 7 | oc set probe dc/inventory --liveness --readiness --initial-delay-seconds=30 --failure-threshold=3 --get-url=http://:8080/node 8 | oc set probe dc/gateway --liveness --readiness --initial-delay-seconds=15 --failure-threshold=3 --get-url=http://:8080/health 9 | oc set probe dc/web --liveness --readiness --get-url=http://:8080/ -------------------------------------------------------------------------------- /solutions/lab-7/GatewayVerticle.java: -------------------------------------------------------------------------------- 1 | package com.redhat.cloudnative.gateway; 2 | 3 | import io.vertx.circuitbreaker.CircuitBreakerOptions; 4 | import io.vertx.core.http.HttpMethod; 5 | import io.vertx.core.json.Json; 6 | import io.vertx.core.json.JsonObject; 7 | import io.vertx.ext.web.client.WebClientOptions; 8 | import io.vertx.rxjava.circuitbreaker.CircuitBreaker; 9 | import io.vertx.rxjava.core.AbstractVerticle; 10 | import io.vertx.rxjava.ext.web.Router; 11 | import io.vertx.rxjava.ext.web.RoutingContext; 12 | import io.vertx.rxjava.ext.web.client.WebClient; 13 | import io.vertx.rxjava.ext.web.codec.BodyCodec; 14 | import io.vertx.rxjava.ext.web.handler.CorsHandler; 15 | import io.vertx.rxjava.ext.web.handler.StaticHandler; 16 | import io.vertx.rxjava.servicediscovery.ServiceDiscovery; 17 | import io.vertx.rxjava.servicediscovery.types.HttpEndpoint; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import rx.Observable; 21 | import rx.Single; 22 | 23 | public class GatewayVerticle extends AbstractVerticle { 24 | private static final Logger LOG = LoggerFactory.getLogger(GatewayVerticle.class); 25 | 26 | private WebClient catalog; 27 | private WebClient inventory; 28 | private CircuitBreaker circuit; 29 | 30 | @Override 31 | public void start() { 32 | 33 | circuit = CircuitBreaker.create("inventory-circuit-breaker", vertx, 34 | new CircuitBreakerOptions() 35 | .setFallbackOnFailure(true) 36 | .setMaxFailures(3) 37 | .setResetTimeout(5000) 38 | .setTimeout(1000) 39 | ); 40 | 41 | Router router = Router.router(vertx); 42 | router.route().handler(CorsHandler.create("*").allowedMethod(HttpMethod.GET)); 43 | router.get("/").handler(StaticHandler.create("assets")); 44 | router.get("/health").handler(ctx -> ctx.response().end(new JsonObject().put("status", "UP").toString())); 45 | router.get("/api/products").handler(this::products); 46 | 47 | ServiceDiscovery.create(vertx, discovery -> { 48 | // Catalog lookup 49 | Single catalogDiscoveryRequest = HttpEndpoint.rxGetWebClient(discovery, 50 | rec -> rec.getName().equals("catalog")) 51 | .onErrorReturn(t -> WebClient.create(vertx, new WebClientOptions() 52 | .setDefaultHost(System.getProperty("catalog.api.host", "localhost")) 53 | .setDefaultPort(Integer.getInteger("catalog.api.port", 9000)))); 54 | 55 | // Inventory lookup 56 | Single inventoryDiscoveryRequest = HttpEndpoint.rxGetWebClient(discovery, 57 | rec -> rec.getName().equals("inventory")) 58 | .onErrorReturn(t -> WebClient.create(vertx, new WebClientOptions() 59 | .setDefaultHost(System.getProperty("inventory.api.host", "localhost")) 60 | .setDefaultPort(Integer.getInteger("inventory.api.port", 9001)))); 61 | 62 | // Zip all 3 requests 63 | Single.zip(catalogDiscoveryRequest, inventoryDiscoveryRequest, (c, i) -> { 64 | // When everything is done 65 | catalog = c; 66 | inventory = i; 67 | return vertx.createHttpServer() 68 | .requestHandler(router::accept) 69 | .listen(Integer.getInteger("http.port", 8080)); 70 | }).subscribe(); 71 | }); 72 | } 73 | 74 | private void products(RoutingContext rc) { 75 | // Retrieve catalog 76 | catalog.get("/api/catalog").as(BodyCodec.jsonArray()).rxSend() 77 | .map(resp -> { 78 | if (resp.statusCode() != 200) { 79 | new RuntimeException("Invalid response from the catalog: " + resp.statusCode()); 80 | } 81 | return resp.body(); 82 | }) 83 | .flatMap(products -> 84 | // For each item from the catalog, invoke the inventory service 85 | Observable.from(products) 86 | .cast(JsonObject.class) 87 | .flatMapSingle(product -> 88 | circuit.rxExecuteCommandWithFallback( 89 | future -> 90 | inventory.get("/api/inventory/" + product.getString("itemId")).as(BodyCodec.jsonObject()) 91 | .rxSend() 92 | .map(resp -> { 93 | if (resp.statusCode() != 200) { 94 | LOG.warn("Inventory error for {}: status code {}", 95 | product.getString("itemId"), resp.statusCode()); 96 | } 97 | return product.copy().put("availability", 98 | new JsonObject().put("quantity", resp.body().getInteger("quantity"))); 99 | }) 100 | .subscribe( 101 | future::complete, 102 | future::fail), 103 | error -> { 104 | LOG.error("Inventory error for {}: {}", product.getString("itemId"), error.getMessage()); 105 | return product; 106 | } 107 | )) 108 | .toList().toSingle() 109 | ) 110 | .subscribe( 111 | list -> rc.response().end(Json.encodePrettily(list)), 112 | error -> rc.response().end(new JsonObject().put("error", error.getMessage()).toString()) 113 | ); 114 | } 115 | } -------------------------------------------------------------------------------- /solutions/lab-7/solve.sh: -------------------------------------------------------------------------------- 1 | ################### 2 | # Lab 7 Solution # 3 | ################### 4 | 5 | DIRECTORY=`dirname $0` 6 | 7 | # Web Auto-Scaler 8 | oc set resources dc/web --limits=cpu=400m,memory=512Mi --requests=cpu=200m,memory=256Mi 9 | oc autoscale dc/web --min 1 --max 5 --cpu-percent=40 10 | 11 | # Add Circuit Breaker to Gateway 12 | cp $DIRECTORY/*.java $DIRECTORY/../../gateway-vertx/src/main/java/com/redhat/cloudnative/gateway 13 | mvn clean fabric8:deploy -f $DIRECTORY/../../gateway-vertx -------------------------------------------------------------------------------- /solutions/lab-8/solve.sh: -------------------------------------------------------------------------------- 1 | ################### 2 | # Lab 8 Solution # 3 | ################### 4 | 5 | # Inventory DB 6 | oc new-app postgresql-persistent \ 7 | --param=DATABASE_SERVICE_NAME=inventory-postgresql \ 8 | --param=POSTGRESQL_DATABASE=inventory \ 9 | --param=POSTGRESQL_USER=inventory \ 10 | --param=POSTGRESQL_PASSWORD=inventory \ 11 | --labels=app=inventory 12 | 13 | # Catalog DB 14 | oc new-app postgresql-persistent \ 15 | --param=DATABASE_SERVICE_NAME=catalog-postgresql \ 16 | --param=POSTGRESQL_DATABASE=catalog \ 17 | --param=POSTGRESQL_USER=catalog \ 18 | --param=POSTGRESQL_PASSWORD=catalog \ 19 | --labels=app=catalog 20 | 21 | # Inventory ConfigMap 22 | cat < ./project-defaults.yml 23 | swarm: 24 | datasources: 25 | data-sources: 26 | InventoryDS: 27 | driver-name: postgresql 28 | connection-url: jdbc:postgresql://inventory-postgresql:5432/inventory 29 | user-name: inventory 30 | password: inventory 31 | EOF 32 | 33 | 34 | oc create configmap inventory --from-file=project-defaults.yml=./project-defaults.yml 35 | oc volume dc/inventory --add --configmap-name=inventory --mount-path=/app/config 36 | oc set env dc/inventory JAVA_ARGS="-s /app/config/project-defaults.yml" 37 | 38 | # Catalog Config Map 39 | cat < ./application.properties 40 | spring.datasource.url=jdbc:postgresql://catalog-postgresql:5432/catalog 41 | spring.datasource.username=catalog 42 | spring.datasource.password=catalog 43 | spring.datasource.driver-class-name=org.postgresql.Driver 44 | spring.jpa.hibernate.ddl-auto=create 45 | EOF 46 | 47 | oc create configmap catalog --from-file=application.properties=./application.properties 48 | oc delete pod -l app=catalog -------------------------------------------------------------------------------- /solutions/lab-9/solve.sh: -------------------------------------------------------------------------------- 1 | 2 | ################### 3 | # Lab 9 Solution # 4 | ################### 5 | 6 | ############################################################################ 7 | # WARNING WARNING # 8 | # WARNING Replace the GitHub Repository URL with WARNING # 9 | # WARNING your own repository in the following (2 places) WARNING # 10 | # WARNING WARNING # 11 | ############################################################################ 12 | 13 | echo "CAUTION: THIS SOLUTION HAS MANUAL STEPS" 14 | 15 | DIRECTORY=`dirname $0` 16 | 17 | # Add to GitHub 18 | pushd $DIRECTORY/../../inventory-wildfly-swarm 19 | git init 20 | git remote add origin https://github.com/YOUR-USERNAME/inventory-wildfly-swarm.git 21 | git add . --all 22 | git commit -m "initial add" 23 | git push -u origin master 24 | 25 | 26 | # Create Jenkinsfile 27 | cat < Jenkinsfile 28 | pipeline { 29 | agent { 30 | label 'maven' 31 | } 32 | stages { 33 | stage('Build JAR') { 34 | steps { 35 | sh "mvn package" 36 | stash name:"jar", includes:"target/inventory-1.0-SNAPSHOT-swarm.jar" 37 | } 38 | } 39 | stage('Build Image') { 40 | steps { 41 | unstash name:"jar" 42 | script { 43 | openshift.withCluster() { 44 | openshift.startBuild("inventory-s2i", "--from-file=target/inventory-1.0-SNAPSHOT-swarm.jar", "--wait") 45 | } 46 | } 47 | } 48 | } 49 | stage('Deploy') { 50 | steps { 51 | script { 52 | openshift.withCluster() { 53 | def dc = openshift.selector("dc", "inventory") 54 | dc.rollout().latest() 55 | dc.rollout().status() 56 | } 57 | } 58 | } 59 | } 60 | } 61 | } 62 | EOF 63 | 64 | git add Jenkinsfile 65 | git commit -m "pipeline added" 66 | git push origin master 67 | 68 | 69 | # Create Pipeline 70 | oc login -u system:admin 71 | oc import-image jenkins:v3.7 --from='registry.access.redhat.com/openshift3/jenkins-2-rhel7:v3.7' --confirm -n openshift 72 | oc export template jenkins-persistent -n openshift -o json | sed 's/jenkins:latest/jenkins:v3.7/g' | oc replace -f - -n openshift 73 | oc export template jenkins-ephemeral -n openshift -o json | sed 's/jenkins:latest/jenkins:v3.7/g' | oc replace -f - -n openshift 74 | oc login -u developer -p developer 75 | oc new-app jenkins-ephemeral 76 | sleep 30 77 | oc new-app . --name=inventory-pipeline --strategy=pipeline 78 | 79 | popd -------------------------------------------------------------------------------- /web-nodejs/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.iml 3 | *.log 4 | *.class 5 | tmp/ 6 | .idea/ 7 | .openshiftio 8 | .settings/ 9 | .classpath 10 | .project 11 | .DS_Store 12 | bower_components/ 13 | node_modules/ -------------------------------------------------------------------------------- /web-nodejs/app/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var module = angular.module('app', ['ngRoute', 'patternfly']), auth = { 4 | loggedIn: false, 5 | ssoEnabled: false, 6 | logout: function () { 7 | } 8 | }; 9 | 10 | module.factory('Auth', function () { 11 | return auth; 12 | }); 13 | 14 | angular.element(document).ready(function () { 15 | 16 | // get config 17 | var initInjector = angular.injector(["ng"]); 18 | var $http = initInjector.get("$http"); 19 | 20 | $http.get("coolstore.json").then(function (response) { 21 | module.constant("COOLSTORE_CONFIG", response.data); 22 | 23 | if (!response.data.SSO_ENABLED) { 24 | angular.bootstrap(document, ["app"], { 25 | strictDi: true 26 | }); 27 | } else { 28 | auth.ssoEnabled = true; 29 | var keycloakAuth = new Keycloak('keycloak.json'); 30 | auth.loggedIn = false; 31 | 32 | auth.login = function () { 33 | keycloakAuth.login({ 34 | loginHint: 'appuser' 35 | }); 36 | }; 37 | 38 | keycloakAuth.init({ 39 | onLoad: 'check-sso' 40 | }).success(function () { 41 | if (keycloakAuth.authenticated) { 42 | keycloakAuth.loadUserInfo().success(function (userInfo) { 43 | auth.userInfo = userInfo; 44 | angular.bootstrap(document, ["app"], { 45 | strictDi: true 46 | }); 47 | auth.loggedIn = true; 48 | auth.authz = keycloakAuth; 49 | auth.logout = function () { 50 | auth.loggedIn = false; 51 | auth.authz = null; 52 | auth.userInfo = {}; 53 | keycloakAuth.logout(); 54 | }; 55 | }).error(function () { 56 | angular.bootstrap(document, ["app"], { 57 | strictDi: true 58 | }); 59 | 60 | }); 61 | } else { 62 | angular.bootstrap(document, ["app"], { 63 | strictDi: true 64 | }); 65 | } 66 | }).error(function (msg) { 67 | angular.bootstrap(document, ["app"], { 68 | strictDi: true 69 | }); 70 | }); 71 | } 72 | }); 73 | }); 74 | 75 | 76 | // setup interceptors 77 | module.config(['$httpProvider', function ($httpProvider) { 78 | 79 | $httpProvider.interceptors.push(['$q', 'Auth', function ($q, Auth) { 80 | return { 81 | 'request': function (config) { 82 | var deferred = $q.defer(); 83 | if (Auth.authz && Auth.authz.token) { 84 | Auth.authz.updateToken(5).success(function () { 85 | config.headers = config.headers || {}; 86 | config.headers.Authorization = 'Bearer ' + Auth.authz.token; 87 | config.withCredentials = true; 88 | deferred.resolve(config); 89 | }).error(function () { 90 | deferred.reject('Failed to refresh token'); 91 | }); 92 | } else { 93 | config.withCredentials = false; 94 | deferred.resolve(config); 95 | } 96 | return deferred.promise; 97 | 98 | }, 99 | 'responseError': function (response) { 100 | if (response.status == 401) { 101 | auth.logout(); 102 | } 103 | return $q.reject(response); 104 | 105 | } 106 | } 107 | }]); 108 | }]); 109 | 110 | -------------------------------------------------------------------------------- /web-nodejs/app/controllers/controllers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app') 4 | 5 | .controller("HomeController", 6 | ['$scope', '$http', '$filter', 'Notifications', 'cart', 'catalog', 'Auth', 7 | function ($scope, $http, $filter, Notifications, cart, catalog, $auth) { 8 | 9 | $scope.products = []; 10 | $scope.addToCart = function (item) { 11 | cart.addToCart(item.product, parseInt(item.quantity)).then(function (data) { 12 | Notifications.success("Added! Your total is " + $filter('currency')(data.cartTotal)); 13 | }, function (err) { 14 | Notifications.error("Error adding to cart: " + err.statusText); 15 | }); 16 | }; 17 | 18 | $scope.isLoggedIn = function () { 19 | return $auth.loggedIn; 20 | }; 21 | $scope.ssoEnabled = function () { 22 | return $auth.ssoEnabled; 23 | }; 24 | 25 | $scope.login = function () { 26 | $auth.login(); 27 | }; 28 | 29 | 30 | // initialize products 31 | catalog.getProducts().then(function (data) { 32 | if (data.error != undefined && data.error != "") { 33 | Notifications.error("Error retrieving products: " + data.error); 34 | return; 35 | } 36 | $scope.products = data.map(function (el) { 37 | return { 38 | quantity: "1", 39 | product: el 40 | } 41 | }) 42 | }, function (err) { 43 | Notifications.error("Error retrieving products: " + err.statusText); 44 | }); 45 | 46 | 47 | }]) 48 | 49 | .controller("CartController", 50 | ['$scope', '$http', 'Notifications', 'cart', 'Auth', 51 | function ($scope, $http, Notifications, cart, $auth) { 52 | 53 | function reset() { 54 | $scope.cart = cart.getCart(); 55 | $scope.items = $scope.cart.shoppingCartItemList; 56 | 57 | $scope.subtotal = 0; 58 | $scope.cart.shoppingCartItemList.forEach(function (item) { 59 | $scope.subtotal += (item.quantity * item.product.price); 60 | }); 61 | } 62 | 63 | $scope.config = { 64 | selectItems: false, 65 | multiSelect: false, 66 | dblClick: false, 67 | showSelectBox: false 68 | }; 69 | 70 | function performAction(action, item) { 71 | cart.removeFromCart(item.product, item.quantity).then(function (newCart) { 72 | reset(); 73 | }, function (err) { 74 | Notifications.error("Error removing from cart: " + err.statusText); 75 | }); 76 | }; 77 | 78 | $scope.actionButtons = [ 79 | { 80 | name: 'Remove', 81 | title: 'Remove', 82 | actionFn: performAction 83 | } 84 | ]; 85 | 86 | 87 | $scope.$watch(function () { 88 | return cart.getCart(); 89 | }, function (newValue) { 90 | reset(); 91 | }); 92 | 93 | $scope.$watch(function () { 94 | return $auth.userInfo; 95 | }, function (newValue) { 96 | cart.reset(); 97 | }); 98 | 99 | $scope.checkout = function () { 100 | cart.checkout().then(function (cartData) { 101 | }, function (err) { 102 | Notifications.error("Error checking out: " + err.statusText); 103 | }); 104 | }; 105 | 106 | $scope.isLoggedIn = function () { 107 | return $auth.loggedIn; 108 | }; 109 | $scope.ssoEnabled = function () { 110 | return $auth.ssoEnabled; 111 | }; 112 | 113 | reset(); 114 | }]) 115 | 116 | .controller("HeaderController", 117 | ['$scope', '$location', '$http', 'Notifications', 'cart', 'Auth', 118 | function ($scope, $location, $http, Notifications, cart, $auth) { 119 | $scope.userInfo = $auth.userInfo; 120 | 121 | $scope.cartTotal = 0.0; 122 | $scope.itemCount = 0; 123 | 124 | $scope.isLoggedIn = function () { 125 | return $auth.loggedIn; 126 | }; 127 | 128 | $scope.login = function () { 129 | $auth.login(); 130 | }; 131 | $scope.logout = function () { 132 | $auth.logout(); 133 | }; 134 | $scope.isLoggedIn = function () { 135 | return $auth.loggedIn; 136 | }; 137 | $scope.ssoEnabled = function () { 138 | return $auth.ssoEnabled; 139 | }; 140 | $scope.profile = function () { 141 | $auth.authz.accountManagement(); 142 | }; 143 | $scope.$watch(function () { 144 | return cart.getCart().cartTotal || 0.0; 145 | }, function (newValue) { 146 | $scope.cartTotal = newValue; 147 | }); 148 | 149 | $scope.$watch(function () { 150 | var totalItems = 0; 151 | cart.getCart().shoppingCartItemList.forEach(function (el) { 152 | totalItems += el.quantity; 153 | }); 154 | return totalItems; 155 | }, function (newValue) { 156 | $scope.itemCount = newValue; 157 | }); 158 | 159 | $scope.$watch(function () { 160 | return $auth.userInfo; 161 | }, function (newValue) { 162 | $scope.userInfo = newValue; 163 | }); 164 | 165 | $scope.isActive = function (loc) { 166 | return loc === $location.path(); 167 | } 168 | }]); 169 | -------------------------------------------------------------------------------- /web-nodejs/app/coolstore.config.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | API_ENDPOINT: 'gateway-' + process.env.OPENSHIFT_BUILD_NAMESPACE, 3 | SECURE_API_ENDPOINT: 'secure-gateway-' + process.env.SECURE_COOLSTORE_GW_SERVICE, 4 | SSO_ENABLED: process.env.SSO_URL ? true : false 5 | }; 6 | 7 | if (process.env.COOLSTORE_GW_ENDPOINT != null) { 8 | config.API_ENDPOINT = process.env.COOLSTORE_GW_ENDPOINT; 9 | } else if (process.env.COOLSTORE_GW_SERVICE != null) { 10 | config.API_ENDPOINT = process.env.COOLSTORE_GW_SERVICE + '-' + process.env.OPENSHIFT_BUILD_NAMESPACE; 11 | } 12 | 13 | 14 | if (process.env.SECURE_COOLSTORE_GW_ENDPOINT != null) { 15 | config.SECURE_API_ENDPOINT = process.env.SECURE_COOLSTORE_GW_ENDPOINT; 16 | } else if (process.env.SECURE_COOLSTORE_GW_SERVICE != null) { 17 | config.SECURE_API_ENDPOINT = process.env.SECURE_COOLSTORE_GW_SERVICE + '-' + process.env.OPENSHIFT_BUILD_NAMESPACE; 18 | } 19 | 20 | module.exports = config; -------------------------------------------------------------------------------- /web-nodejs/app/css/coolstore.css: -------------------------------------------------------------------------------- 1 | .media img { 2 | height: 100px; 3 | } 4 | 5 | .p-t-8 { 6 | padding-top: 8px !important; 7 | } 8 | 9 | [ng-click], 10 | [data-ng-click], 11 | [x-ng-click] { 12 | cursor: pointer; 13 | } -------------------------------------------------------------------------------- /web-nodejs/app/directives/header.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | angular.module('app').directive('header', function() { 5 | return { 6 | restrict: 'A', 7 | replace: true, 8 | templateUrl: 'partials/header.html', 9 | controller: 'HeaderController' 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /web-nodejs/app/imgs/16 oz. Vortex Tumbler.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/web-nodejs/app/imgs/16 oz. Vortex Tumbler.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/Forge Laptop Sticker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/web-nodejs/app/imgs/Forge Laptop Sticker.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/Lytro Camera.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/web-nodejs/app/imgs/Lytro Camera.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/Oculus Rift.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/web-nodejs/app/imgs/Oculus Rift.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/Ogio Caliber Polo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/web-nodejs/app/imgs/Ogio Caliber Polo.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/Pebble Smart Watch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/web-nodejs/app/imgs/Pebble Smart Watch.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/Red Fedora.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/web-nodejs/app/imgs/Red Fedora.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/Solid Performance Polo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/web-nodejs/app/imgs/Solid Performance Polo.jpg -------------------------------------------------------------------------------- /web-nodejs/app/imgs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-labs/cloud-native-labs/81704a4b1c6a04bd8eda834a886443a8963115a5/web-nodejs/app/imgs/logo.png -------------------------------------------------------------------------------- /web-nodejs/app/keycloak.config.js: -------------------------------------------------------------------------------- 1 | var config = 2 | { 3 | "auth-server-url" : process.env.SSO_URL, 4 | "realm": process.env.SSO_REALM, 5 | "realm-public-key": process.env.SSO_PUBLIC_KEY , 6 | "resource": process.env.SSO_CLIENT_ID, 7 | "ssl-required": 'external', 8 | "public-client": true, 9 | "enable-cors": true 10 | }; 11 | 12 | module.exports = config; -------------------------------------------------------------------------------- /web-nodejs/app/routes/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('app').config([ '$routeProvider', function($routeProvider) { 4 | $routeProvider.when('/', { 5 | templateUrl : 'partials/home.html', 6 | controller : 'HomeController' 7 | }).when('/cart', { 8 | templateUrl : 'partials/cart.html', 9 | controller : 'CartController' 10 | }).otherwise({ 11 | redirectTo : '/' 12 | }); 13 | } ]); 14 | -------------------------------------------------------------------------------- /web-nodejs/app/services/cart.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module("app") 4 | 5 | .factory('cart', ['$http', '$q', 'COOLSTORE_CONFIG', 'Auth', '$location', function($http, $q, COOLSTORE_CONFIG, $auth, $location) { 6 | var factory = {}, cart, products, cartId, baseUrl; 7 | if ($location.protocol() === 'https') { 8 | baseUrl = (COOLSTORE_CONFIG.SECURE_API_ENDPOINT.startsWith("https://") ? COOLSTORE_CONFIG.SECURE_API_ENDPOINT : "https://" + COOLSTORE_CONFIG.SECURE_API_ENDPOINT + '.' + $location.host().replace(/^.*?\.(.*)/g,"$1")) + '/api/cart'; 9 | } else { 10 | baseUrl = (COOLSTORE_CONFIG.API_ENDPOINT.startsWith("http://") ? COOLSTORE_CONFIG.API_ENDPOINT : "http://" + COOLSTORE_CONFIG.API_ENDPOINT + '.' + $location.host().replace(/^.*?\.(.*)/g,"$1")) + '/api/cart'; 11 | } 12 | 13 | factory.checkout = function() { 14 | var deferred = $q.defer(); 15 | $http({ 16 | method: 'POST', 17 | url: baseUrl + '/checkout/' + cartId 18 | }).then(function(resp) { 19 | cart = resp.data; 20 | deferred.resolve(resp.data); 21 | }, function(err) { 22 | deferred.reject(err); 23 | }); 24 | return deferred.promise; 25 | }; 26 | 27 | factory.reset = function() { 28 | cart = { 29 | shoppingCartItemList: [] 30 | }; 31 | var tmpId = localStorage.getItem('cartId'); 32 | var authId = $auth.userInfo ? $auth.userInfo.sub : null; 33 | 34 | if (tmpId && authId) { 35 | // transfer cart 36 | cartId = authId; 37 | this.setCart(tmpId).then(function(result) { 38 | localStorage.removeItem('cartId'); 39 | }, function(err) { 40 | console.log("could not transfer cart " + tmpId + " to cart " + authId + ": " + err); 41 | }); 42 | return; 43 | } 44 | 45 | if (tmpId && !authId) { 46 | cartId = tmpId; 47 | } 48 | 49 | if (!tmpId && authId) { 50 | cartId = authId; 51 | } 52 | 53 | if (!tmpId && !authId) { 54 | tmpId = 'id-' + Math.random(); 55 | localStorage.setItem('cartId', tmpId); 56 | cartId = tmpId; 57 | } 58 | 59 | cart.shoppingCartItemList = []; 60 | $http({ 61 | method: 'GET', 62 | url: baseUrl + '/' + cartId 63 | }).then(function(resp) { 64 | cart = resp.data; 65 | }, function(err) { 66 | }); 67 | 68 | }; 69 | 70 | factory.getCart = function() { 71 | return cart; 72 | }; 73 | 74 | factory.removeFromCart = function(product, quantity) { 75 | var deferred = $q.defer(); 76 | $http({ 77 | method: 'DELETE', 78 | url: baseUrl + '/' + cartId + '/' + product.itemId + '/' + quantity 79 | }).then(function(resp) { 80 | cart = resp.data; 81 | deferred.resolve(resp.data); 82 | }, function(err) { 83 | deferred.reject(err); 84 | }); 85 | return deferred.promise; 86 | 87 | }; 88 | 89 | factory.setCart = function(id) { 90 | var deferred = $q.defer(); 91 | $http({ 92 | method: 'POST', 93 | url: baseUrl + '/' + cartId + '/' + id 94 | }).then(function(resp) { 95 | cart = resp.data; 96 | deferred.resolve(resp.data); 97 | }, function(err) { 98 | deferred.reject(err); 99 | }); 100 | return deferred.promise; 101 | 102 | }; 103 | 104 | factory.addToCart = function(product, quantity) { 105 | var deferred = $q.defer(); 106 | $http({ 107 | method: 'POST', 108 | url: baseUrl + '/' + cartId + '/' + product.itemId + '/' + quantity 109 | }).then(function(resp) { 110 | cart = resp.data; 111 | deferred.resolve(resp.data); 112 | }, function(err) { 113 | deferred.reject(err); 114 | }); 115 | return deferred.promise; 116 | 117 | }; 118 | 119 | factory.reset(); 120 | return factory; 121 | }]); 122 | -------------------------------------------------------------------------------- /web-nodejs/app/services/catalog.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module("app") 4 | 5 | .factory('catalog', ['$http', '$q', 'COOLSTORE_CONFIG', 'Auth', '$location', function($http, $q, COOLSTORE_CONFIG, $auth, $location) { 6 | var factory = {}, products, baseUrl; 7 | 8 | if ($location.protocol() === 'https') { 9 | baseUrl = (COOLSTORE_CONFIG.SECURE_API_ENDPOINT.startsWith("https://") ? COOLSTORE_CONFIG.SECURE_API_ENDPOINT : "https://" + COOLSTORE_CONFIG.SECURE_API_ENDPOINT + '.' + $location.host().replace(/^.*?\.(.*)/g,"$1")) + '/api/products'; 10 | } else { 11 | baseUrl = (COOLSTORE_CONFIG.API_ENDPOINT.startsWith("http://") ? COOLSTORE_CONFIG.API_ENDPOINT : "http://" + COOLSTORE_CONFIG.API_ENDPOINT + '.' + $location.host().replace(/^.*?\.(.*)/g,"$1")) + '/api/products'; 12 | } 13 | 14 | factory.getProducts = function() { 15 | var deferred = $q.defer(); 16 | if (products) { 17 | deferred.resolve(products); 18 | } else { 19 | $http({ 20 | method: 'GET', 21 | url: baseUrl 22 | }).then(function(resp) { 23 | products = resp.data; 24 | deferred.resolve(resp.data); 25 | }, function(err) { 26 | deferred.reject(err); 27 | }); 28 | } 29 | return deferred.promise; 30 | }; 31 | 32 | return factory; 33 | }]); 34 | -------------------------------------------------------------------------------- /web-nodejs/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coolstore-microservice", 3 | "description": "Web UI for the CoolStore Microservices App", 4 | "authors": [ 5 | "Red Hat" 6 | ], 7 | "license": "Apache-2.0", 8 | "private": true, 9 | "devDependencies": { 10 | "patternfly": "3.17.0", 11 | "angular-patternfly": "3.17.0", 12 | "bootstrap-switch": "3.3.2", 13 | "isotope": "2.2.2", 14 | "imagesloaded": "4.1.1", 15 | "angular-route": "1.5.10", 16 | "keycloak": "1.9.8" 17 | }, 18 | "resolutions": { 19 | "angular": "1.5.10", 20 | "jquery": "3.3.1", 21 | "bootstrap": "~3.3.7" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /web-nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Red Hat", 3 | "contributors": [ 4 | "James Falkner (https://developers.redhat.com)" 5 | ], 6 | "name": "web", 7 | "repository": "jbossdemocentral/coolstore-microservice", 8 | "version": "1.0.0", 9 | "license": "Apache-2.0", 10 | "private": true, 11 | "description": "Web UI for the CoolStore Microservices App", 12 | "homepage": "https://developers.redhat.com", 13 | "dependencies": { 14 | "cors": "^2.8.3", 15 | "express": "^4.13.4", 16 | "keycloak-connect": "^3.1.0", 17 | "request": "^2.74.0" 18 | }, 19 | "devDependencies": { 20 | "bower": ">=1.7.9", 21 | "bower-nexus3-resolver": "*" 22 | }, 23 | "engines": { 24 | "node": ">=0.10.10" 25 | }, 26 | "scripts": { 27 | "postinstall": "node_modules/.bin/bower --config.registry.search=${BOWER_MIRROR} install" 28 | } 29 | } -------------------------------------------------------------------------------- /web-nodejs/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | http = require('http'), 3 | request = require('request'), 4 | fs = require('fs'), 5 | app = express(), 6 | path = require("path"), 7 | keycloakConfig = require('./app/keycloak.config.js'), 8 | coolstoreConfig = require('./app/coolstore.config.js'), 9 | Keycloak = require('keycloak-connect'), 10 | cors = require('cors'); 11 | 12 | 13 | var port = process.env.PORT || process.env.OPENSHIFT_NODEJS_PORT || 8080, 14 | ip = process.env.IP || process.env.OPENSHIFT_NODEJS_IP || '0.0.0.0', 15 | secport = process.env.PORT || process.env.OPENSHIFT_NODEJS_PORT || 8443; 16 | 17 | // Enable CORS support 18 | app.use(cors()); 19 | 20 | // error handling 21 | app.use(function(err, req, res, next) { 22 | console.error(err.stack); 23 | res.status(500).send('Something bad happened!'); 24 | }); 25 | 26 | // keycloak config server 27 | app.get('/keycloak.json', function(req, res, next) { 28 | res.json(keycloakConfig); 29 | }); 30 | // coolstore config server 31 | app.get('/coolstore.json', function(req, res, next) { 32 | res.json(coolstoreConfig); 33 | }); 34 | 35 | app.use(express.static(path.join(__dirname, '/views'))); 36 | app.use('/app', express.static(path.join(__dirname, '/app'))); 37 | app.use('/bower_components', express.static(path.join(__dirname, '/bower_components'))); 38 | 39 | console.log("coolstore config: " + JSON.stringify(coolstoreConfig)); 40 | console.log("keycloak config: " + JSON.stringify(keycloakConfig)); 41 | 42 | 43 | http.createServer(app).listen(port); 44 | 45 | console.log('HTTP Server running on http://%s:%s', ip, port); 46 | 47 | module.exports = app; -------------------------------------------------------------------------------- /web-nodejs/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Red Hat CoolStore Microservices App 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /web-nodejs/views/partials/cart.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 |
8 | 9 |
10 | 11 |
12 |
13 | {{item.product.name}} 14 |
15 |
16 | 17 |

{{item.product.name}}

18 |
19 | Quantity: {{item.quantity}} 20 |
21 |
22 | {{item.product.description}} 23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |

You have not added any items to your Shopping Cart!

31 | Return to Store 32 |
33 |
34 |
35 | 36 |
37 |
38 |

Shopping Summary

39 |
40 |
41 | 42 |

Cart Total: {{subtotal | currency}}

43 |

Promotional Item Savings: {{cart.cartItemPromoSavings | currency}} 44 |

45 |

Subtotal: {{cart.cartItemTotal | currency}}

46 |

Shipping: {{cart.shippingTotal | currency}}

47 |

Promotional Shipping Savings: {{cart.shippingPromoSavings | currency}} 48 |

49 |

50 | Total Order Amount: {{cart.cartTotal | currency}} 51 |

52 |
53 | 62 |
63 |
64 | 65 | 66 | -------------------------------------------------------------------------------- /web-nodejs/views/partials/header.html: -------------------------------------------------------------------------------- 1 | 51 | -------------------------------------------------------------------------------- /web-nodejs/views/partials/home.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | 7 |
8 |
9 | 10 |
11 |
12 | 13 |
14 |

{{item.product.price | currency}}

15 |
16 |   17 |
18 | 19 |
 Not in Stock
20 |
21 | 22 |
23 |
24 | 31 | 32 |
33 | 34 |
35 |
36 |
37 |
38 |
39 | 40 |
41 | 42 | --------------------------------------------------------------------------------