├── .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 [](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 |
Catalog Service
17 |
This is a Golang Microservice for the CoolStore Demo. (Test it)
18 |
19 |
20 |
21 |
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 |
Catalog Service
17 |
This is a Spring Boot Microservice for the CoolStore Demo. (Test it)
18 |
19 |
20 |
21 |
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 |
Gateway Service
17 |
This is a Vert.x Microservice for the CoolStore Demo. (Test it)
18 |
19 |
20 |
21 |
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 |
Inventory Service
17 |
This is a Thorntail Microservice for the CoolStore Demo. (Test it)
18 |
19 |
20 |
21 |
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 |
Inventory Service
17 |
This is a Vert.x Microservice for the CoolStore Demo. (Test it)
18 |
19 |
20 |
21 |
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 |
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 |
67 |
68 |
69 |
75 |
76 |
Thank you for your order!
77 |
78 | Your order total of {{cart.cartTotal | currency}} will be processed when you click Checkout.
79 |
80 |
81 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/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 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------